Pray for Vim

gg

僕の青春を支えてくれたテキストエディタであるVimに感謝を込めたブログです。Vimだけではなくその他ツールの紹介もしています。Vimの操作性、Vimがもたらした影響などを機能とともに紹介できればと思います。

Vimを使おう

Vimはキーボード操作に徹底的に特化したテキスト編集機能を持つテキストエディタです。

テキストエディタの中でもemacsと同類のCLIという分類に入り、キーストロークだけですべての操作が完結します。

プログラマーの作業はマウスに触れる時間が少なければ少ないほど洗練されます。

まずはVimの基本的なカーソル移動である「hjkl」に慣れましょう。

次に「ft(FT)」で任意の文字列までひとっ飛びする技を会得しましょう、「;,」も駆使することを忘れずに。

/文字列」で文字列を探せます。「n(N)」と.(ドット)との相性は抜群です。

d(delete), c(cut), y(yank), p(paste)も駆使しましょう。ciwは頻出です。

i, I, a, A, o, Oでインサートモードに入れます。

ctrl-vshift-vの選択モードの挙動の違いを理解できてますか?

.(ドット), x, sも便利ですね。

マクロって使ったことありますか? qから任意のキーを押すと操作を記憶し始めます。もう一度qを押すと記録終了。qの後に押したキーを押すと先ほどの操作が丸々実行されます。ある程度構造化されたファイルを編集するときに真価を発揮します。

ターミナル操作もVim

tmuxを入れて以下のようなファイルを設定してください。

.tmux.conf

# tmux mode
set-option -g prefix C-t
unbind C-b

# transition of panes
bind -r h select-pane -L
bind -r j select-pane -D
bind -r k select-pane -U
bind -r l select-pane -R

# adjustment of pane size
bind -r H resize-pane -L 2
bind -r J resize-pane -D 2 
bind -r K resize-pane -U 2 
bind -r L resize-pane -R 2

# split the current pane virtically with |
bind | split-window -h
# split the current pane horisontally with -
bind - split-window -v

# use vi keybinds in the copy mode
setw -g mode-keys vi
bind -T copy-mode-vi v send -X begin-selection

これで「ctrl-t + -」でペインを横に分割、「ctrl-t + |」で縦に分割できます。そして、ペイン間の移動を「ctrl-hjkl」で移動でき、ペインのサイズも「ctrl-HJKL」で調節できます。「ctrl-[」で選択モードになり。vまたはVで選択方法を変えて、enterでヤンクできます。

ブラウザもVimライクに

Vimiumというプラグインをブラウザに入れるとhjklでのスクロール操作、fでクリッカブルなエレメントの候補にキーが割り振れられ、キーを入力するとクリック操作が行われます。

OSのGUI操作もVimライクに

Homerowというアプリを入れればVimiumっぽくOSのGUI操作ができます。

マウス操作も最悪Vim

マウスに手をのばすことは邪道ですが、GUI操作が避けて通れないこともあるでしょう。それならばkarabinerを入れて、以下のような設定を追加してください。

github.com

ZZ

Vimの威力や、GUI操作にVimベースのインターフェースを提供するツールの便利さが伝わればと思います。Vimは高速な操作を追求した先に至る極地です。触ったことがない人もこれを機会に触ってみてください。


最近、Vimの開発者であるBram Moolenaarさんがお亡くなりになりました。彼の訃報に接し、彼が開発した偉大なエディタを称える記事を書かせていただきました。

Farewell, Vim's visionary: Bram Moolenaar.

自分の研究内容(DBMS実装)に関する最強の本一式揃えた

最強の本一式

揃えたぞー!!

アフィリンクとかないので純粋に揃えたことをアピールしたい。書評することになる本の予告です。他にもおすすめがあればリコメンド待ってます!

結構それぞれ高いので、学生なら大学の図書館に取り寄せてもらうか、社会人なら気前よく購入!!という感じでいいんじゃないかと思います

Designing Data-Intensive Applications

分散システムといえば!!!!この本一択!!! 一応日本語もある。一般的なエンジニアにもおすすめされている本

イノシシ本と呼ばれている

https://www.amazon.co.jp/-/en/Martin-Kleppmann/dp/1449373321

Transaction Processing Concepts and Techniques

トランザクション技術といえば...この本!! といっても当初は辞書みたいに使うのだろうなーという感じ、玄人向け

通称なんと呼ばれいるのかわからない

辞書っぽく使う予定

https://www.amazon.co.jp/gp/product/B016W7HLX8/ref=dbs_a_def_rwt_hsch_vapi_taft_p1_i0

Database Design and Implementation

SimpleDBというDisk-OrientedなRDBMSJavaで実装して、理屈+実装が解説されている本

SimpleDB本とよく呼ばれている

今現在これを読み進めているけど、コード量を抑えるためにナイーブな実装になっているので、読み終えてからがDBMS実装のスタートな感じがする

具体的には、今まで読み進めてきた範囲での実装について話すと、リカバリプロトコルがARIESじゃなくてundo-onlyとか、バッファ管理がClock(-sweep)などじゃなくてシーケンシャルにピン留めされていないバッファを探す実装とか、etc.

あと、受け売りになるが、この本ではB-treeを0から書けるようになるほどの十分な説明がない+Mergeが実装されていなかったり、ロックの粒度についても色々妥協点が見られるらしい

でもすごく読んでいて楽しいし勉強になるのですごく推している

https://www.amazon.co.jp/Database-Design-Implementation-Data-Centric-Applications/dp/3030338355

The Art of Multiprocessor Programming

並行プログラミングの本、これも辞書っぽく使う予定

TAoMP本と呼ばれている

https://www.amazon.co.jp/Art-Multiprocessor-Programming-Second/dp/0124159508

ちなみに日本語の本で「並行プログラミング入門」という本があって、扱う内容がちょっと似ていて日本語の本なので読みやすいらしい

https://www.amazon.co.jp/%E4%B8%A6%E8%A1%8C%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E5%85%A5%E9%96%80-%E2%80%95Rust%E3%80%81C%E3%80%81%E3%82%A2%E3%82%BB%E3%83%B3%E3%83%96%E3%83%AA%E3%81%AB%E3%82%88%E3%82%8B%E5%AE%9F%E8%A3%85%E3%81%8B%E3%82%89%E3%81%AE%E3%82%A2%E3%83%97%E3%83%AD%E3%83%BC%E3%83%81-%E9%AB%98%E9%87%8E-%E7%A5%90%E8%BC%9D/dp/4873119596

Modern B-Tree Techniques

B-treeの本、B-treeを説明するために200ページも使っている本、やべえ

SimpleDB本だけじゃB-treeを理解できないと思うので必要だと思った

https://www.amazon.co.jp/Modern-B-Tree-Techniques-Foundations-Databases/dp/1601984820

Database Internals

データベースエンジンの内部の話をしている、この本さえあればDBMS作れるかというと決してないけど、上記の本を読み尽くしたら結構すっと内容が入る感じ

辞書っぽく使う予定

https://www.amazon.co.jp/Database-Internals-Distributed-Systems-English-ebook/dp/B07XW76VHZ

揃えたんだけど...

読み終えてスタートなんでね...積読を消化する旅はこれから!!!

はてなブログのカスタムCSSで遊びませう

はてなブログのカスタムCSSで遊びませう

はてなブログの装いを編み出すことは、諸兄もご承知のとおりでございましょう。しかしながら、カスタムCSSを自在にいじくり回すことができた経験をお持ちの方は、そう多くはあるまいかと申し上げたく存じます。

こちらに示すようなカスタムCSSの手法を取り入れれば、現在の拙者のブログのような、風変わりで面白い趣向を取り入れることもできるのでございます。

/* <system section="theme" selected="smooth"> */
@import "/css/theme/smooth/smooth.css";
/* </system> */

<style>
  html {
    height: 100%;
    overflow: hidden;
    background-color: #000;
  }

  html:before,
  html:after,
  body:before,
  body:after {
    content: "";
    position: fixed;
    top: 0;
    left: 0;
    width: 100px;
    height: 100px;
    background-image: url("https://pbs.twimg.com/profile_images/1243175796685983745/efRnTYKv_400x400.jpg");
    background-repeat: no-repeat;
    background-size: cover;
    animation: rollingIcon 8s linear infinite;
    opacity: 0;
    border-radius: 50%;
  }

  @keyframes rollingIcon {
    0% {
      opacity: 1;
      top: 4%;
      left: 0;
      transform: ranslateX(0%) rotate(0deg);
    }
    5% {
      top: 4%;
      left: 20%;
      transform: translateX(-20%) rotate(-90deg);
    }
    10% {
      top: 4%;
      left: 40%;
      transform: translateX(-40%) rotate(-180deg);
    }
    15% {
      top: 4%;
      left: 60%;
      transform: translateX(-60%) rotate(-270deg);
    }
    20% {
      top: 4%;
      left: 80%;
      transform: translateX(-80%) rotate(-360deg);
    }
    25% {
      top: 4%;
      left: 100%;
      transform: translate(-100%, 0%) rotate(-450deg);
    }
    30% {
      top: 23.2%;
      left: 100%;
      transform: translate(-100%, -20%) rotate(-540deg);
    }
    35% {
      top: 42.4%;
      left: 100%;
      transform: translate(-100%, -40%) rotate(-630deg);
    }
    40% {
      top: 61.6%;
      left: 100%;
      transform: translate(-100%, -60%) rotate(-720deg);
    }
    45% {
      top: 80.8%;
      left: 100%;
      transform: translate(-100%, -80%) rotate(-810deg);
    }
    50% {
      top: 100%;
      left: 100%;
      transform: translate(-100%, -100%) rotate(-900deg);
    }
    55% {
      top: 100%;
      left: 80%;
      transform: translate(-80%, -100%) rotate(-990deg);
    }
    60% {
      top: 100%;
      left: 60%;
      transform: translate(-60%, -100%) rotate(-1080deg);
    }
    65% {
      top: 100%;
      left: 40%;
      transform: translate(-40%, -100%) rotate(-1170deg);
    }
    70% {
      top: 100%;
      left: 20%;
      transform: translate(-20%, -100%) rotate(-1260deg);
    }
    75% {
      top: 100%;
      left: 0;
      transform: translateY(-100%) rotate(-1350deg);
    }
    80% {
      top: 80%;
      left: 0;
      transform: translateY(-80%) rotate(-1440deg);
    }
    85% {
      top: 60%;
      left: 0;
      transform: translateY(-60%) rotate(-1530deg);
    }
    90% {
      top: 40%;
      left: 0;
      transform: translateY(-40%) rotate(-1620deg);
    }
    95% {
      top: 20%;
      left: 0;
      transform: translateY(-20%) rotate(-1710deg);
    }
    100% {
      opacity: 1;
      top: 4%;
      left: 0;
      transform: rotate(-360deg);
    }
  }

  html:after {
    animation-delay: 2s;
  }
  body:before {
    animation-delay: 4s;
  }
  body:after {
    animation-delay: 6s;
  }
</style>

酒で失敗した

前置き

ブログにできる程度の失敗です、記憶がないパートは人から聞いた話です。そのとき一緒にいた人は大笑いしてくれたみたい、良かった...。この程度に済んだのは、すべては介抱組のおかげです。

あらすじ

4/9にy20、y21で10人規模の飲み会をしました。B1, B2, B3はコロナ関係で飲み会ができなくて自分の酒の限界を知りませんでした。(時空歪ませてる民なのでB1からお酒が飲めたよ!)

その会では限界を知ってみたかったので、いっぱい飲んでみました。

場所は「くぅ(居酒屋)」→「うごまち(居酒屋)」→「まねきねこ(カラオケ)」

時系列ごとの解説

くぅ(記憶あり)

理性はありました。ビールジョッキ3杯ほかにフルーツサワー的なのを飲んだ。

うごまち(入ったあたりから記憶ない)

理性を失いました。記憶ないです。泡盛調子乗って飲んでたみたい。

隣の同級生(男)の手にチューしてたらしい。

女の子たちに向かって「美女!」って言いまくってたらしい。

まねきねこ(記憶ない)

まねきねこに行く途中、穴に落ちたりしたみたいです。

人のスト缶飲んでたらしい。

サワヤンって言われるくらいテーブルバンバン叩いてたっぽい。

ポテチの袋をぶっ叩いてたらしい。

お茶が入ったコップをテーブルにひっくり返して、その状態でテーブル叩いてみんなの顔に水しぶきがかかってたらしい。

トイレで吐かせてもらって、鼻血が出て介抱組が顔を洗ってくれたときに抵抗してたらしい。

「俺はいいからお前ら逃げろ!!土日に捕まる!!」という意味不明のセリフを吐いてたらしい。

帰り(記憶ない)

朝になってたらしいが、知らない人に「おはようございます!!! いい天気ですねえ!!」って叫んでたらしい。

翌日

午後2時に起きましたが、午後9時まで吐いてました。

左足は捻挫していて、膝、脛、手、腕は打撲の跡がありました。

振り返り

介抱組へ

介抱組ありがとう...。今度メッシ奢るよ。

同級生へ

楽しかったという感情しか思い出せない。3年間なかった青春を取り戻せた気がするよ。ありがとう。これからもよろしく。

後輩へ

「OS助けてくれた英雄」→「サワヤン」にイメージが降格したと思いますが、これからも尊敬してくださいw

落とし物探してくれた後輩へ

落とし物が発覚したとき探してくれてありがとう。

反省点

  • 泡盛おかわりしない
  • 寝る前に水飲もう

注釈

2023/04/11 修正: 「楽しかった以外の感情しか思い出せない」 → 「楽しかったという感情しか思い出せない」

2023/04/13 追加(まねきねこ):「トイレで吐かせてもらって、鼻血が出て介抱組が顔を洗ってくれたときに抵抗してたらしい。

「俺はいいからお前ら逃げろ!!土日に捕まる!!」という意味不明のセリフを吐いてたらしい。」

学生としてYAPC::Kyoto 2023に参加した話

1. この記事について

著者、Yoshisaurが参加したYAPC::Kyoto 2023に関するブログです。

ブログを書くまでがYAPC

ということで、自分がYAPCで体験したことを皆さん共有できればと思います。

2. ブログの著者、Yoshisaurについて

https://pbs.twimg.com/profile_images/1243175796685983745/efRnTYKv_400x400.jpg

沖縄の琉球大学情報工学を学んでいる大学生です。 好きなエディタはVim、好きな言語はGolang、Web、機械学習、低レイヤーあたりの技術に関心が高いです。

Twitterアカウントです。是非、フォローしてくださいませ。

以降がブログ記事のメインのコンテンツとなります。

3. YAPC::Kyoto 2023とは

3.1. そもそもYAPCとは

YAPCとは...

YAPCはYet Another Perl Conferenceの略で、Perlを軸としたITに関わる全ての人のためのカンファレンスです。 Perlだけにとどまらない技術者たちが、好きな技術の話をし交流するカンファレンスで、技術者であれば誰でも楽しめるお祭りです!」(YAPC:: Kyoto 2023. https://yapcjapan.org/2023kyoto/ )

とのことです。

技術者であれば誰でも楽しめるお祭り」というのは本当にそうで、自分はPerlが全然わからない人だけどかなり楽しめました。

基本的な流れとしては、登壇者orゲストが20分から40分までくらいの時間で好きな技術やトピックについて話をして参加者と交流をするというものです。

同じ時刻に3人の登壇者が別々の部屋で発表、交流が行われていました。

ちなみにYAPCの読み方は...

↓リプライ

このYAPCのコミュニケーションが軽い空気感、素晴らしいな...。

3.2. 2023年のYAPCの開催地(京都)、会場について

2023年のYAPCの開催地は京都でした。

歴史的な建造物や美しい街並みに圧倒されました。

京都はYAPC開催日(3月19日])早朝は摂氏4.9度、昼で17.5度と沖縄に住んでいる学生からするとかなり寒い気温でした。

↓京都駅

京都駅

会場は京都リサーチパークです。

京都駅から徒歩で30分ほどの距離でした。実は最寄りの駅が、京都駅から嵯峨野線に乗って2駅のJR丹波口駅だったのですが、歩きました。

↓会場、京都リサーチパークの中の様子

ワイワイした雰囲気で包まれて、そこにいるだけで楽しかったです。

3.3. 学生旅費支援制度について

YAPCは、学生に対して参加ハードルを下げるために、「学生用の無料参加チケットの販売」や「旅費支援(5万まで)」などの支援制度が設けられています。

blog.yapcjapan.org

自分はこの制度を使って、YAPCに参加させていただきました。金銭面の他に、学生限定でスポンサー企業の方々とのランチという贅沢なイベントも用意されていました。(後述)

4. YAPC参加応募のきっかけから、YAPC終了まで

4.1. 大学の先輩から「もし良かったらYAPCに来てね」と宣伝が来る

僕がB1の頃、大変お世話になった同じ学科の先輩のアナグラさんに「YAPCの学生旅費支援制度の申し込みフォームが開かれたので応募してみては?」とまたっくさん(以下またゆー)と一緒に声をかけていただきました。

2人とも応募フォームを入力して採択結果を待ちました。応募フォームの入力は真面目に記入したといえど、ささっと軽くスムーズに終わらせた感じでした。

ちなみに、学生旅費支援制度の応募フォームの入力締め切りは「2023年1月27日(金)23:59 (JST)」でした。

4.2. 学生旅費支援の採択結果メールを受け取る

学生旅費支援制度の応募フォームの入力締め切りからちょうど一週間後の2023年2月3日に学生旅費支援の採択結果が書かれてるメールが運営から届きました。

結果合格でウキウキになっていました。 YAPC開催日(2023年3月19日)まで6週間強くらいある期間があったので余裕をもって準備を進めることができました

4.3. YAPC開催前日(3月18日)、沖縄から関西に移動、プチ観光

結果的には、YAPC開催前日は移動日に終わりましたが、合間合間の時間でプチ観光を楽しみました。関西空港に到着したのが、午後1時35分でした。空港でまたゆーと合流後に移動がてらプチ観光しました。自由時間(移動時間+観光)の時間は10時間くらいでした。

4.4. YAPC開催当日

4.4.1. 入場+参加景品を貰う

入場と同時にスタッフから参加景品をもらいました。全部ブログに載せきれないほどたくさん貰いました。文字だけで表現すると、トートバック、水筒、スマホ立て、お菓子、正論パンチグローブ、etc.。

中でも気に入っているのは「正論パンチグローブ」です。パワーを感じます。

4.4.2. 沖縄で以前お世話になった方々と感動の再開

入場時に沖縄で以前お世話になったgomaさんとjogoに会えました。

YAPC当日は、運営側に2人、参加者側に2人、僕と面識がある方がいたので、入場後すぐに緊張がほぐれて居心地が良かったです。YAPCに参加する際は色々身の回りの人達を誘って巻き込んでいくといいのかなと思います。

4.4.3. 登壇者、ゲストのトークを聞く

YAPCのメインイベントです。当日のタイムテーブルはこちらです。一度に3人が別々の部屋で登壇するので頑張って一番興味のある話を選びます。

自分の当日のスケジュール

話のタイトル 登壇の時刻 / 長さ 登壇者 スライドURL
春のエンジニア ぶつかり稽古 2023 9:50 / 45分 kan / kenchan スライドなし
ジョブキューシステムFireworqのアーキテクチャ設計と運用時のベストプラクティス 10:40 / 20分 伊奈 林太郎 https://speakerdeck.com/tarao/fireworq
my$talk=qr{\b((?:ir)?reg(?:ular )?exp(?:ressions?)?)\b}ig; 11:10 / 20分 Dan Kogai https://speakerdeck.com/dankogai/my-talk-equals-qr-ir-reg-ular-exp-ressions-ig
あの日ハッカーに憧れた自分が、「ハッカーの呪縛」から解き放たれるまで 12:25 / 40分 あらたま https://speakerdeck.com/ar_tama/anori-hatukanichong-retazi-fen-ga-hatukanozhou-fu-karajie-kifang-tarerumade
【学生限定】スポンサー企業の方々とのランチ 13:10 / 60分 Yasuhiro Onishi(株式会社はてな) / Tatsuro Hisamori(合同会社もりたつ技商) / わいとん (合同会社 Y.pm) スライドなし
ソフトウェアエンジニアリングサバイバルガイド: 廃墟を直す、廃墟を出る、廃墟を壊す、あるいは廃墟に暮らす、廃墟に死す 14:10 / 40分 川上 大喜 https://docs.google.com/presentation/d/1hDY2pb-nYVSLr0HrtQ4EVyrDU4QGgwp4-VRG-Rf26DA/edit
4PB(ペタバイト)を超えるオブジェクトストレージをハードウェアからアプリケーションにかけて運用するノウハウ 15:00 / 40分 三上 烈史(みかみ つよし) https://speakerdeck.com/rsym1290/4petabaitowochao-eruobuziekutosutoreziwo-hadoueakaraapurikesiyonnikakete-yun-yong-surunouhau
法と技術の交差点 16:05 / 50分 上原 哲太郎 / 宮脇 正晴 スライドなし

全て面白いトーク/イベントでした。1日の密度ではなかったです。

4.4.3.1. 個人的に特に面白いと思ったトーク/イベント

4.4.3.1.1. ジョブキューシステムFireworqのアーキテクチャ設計と運用時のベストプラクティス

Go製で、ストレージがMySQLで、at-least-onceなジョブキューシステム、「Fireworq」についてのトークでした。

従来のジョブキューソリューションはTheSchwartz + WorkerManagerだったのですが、困りどころの一つに「ワーカー並列数を増やすと詰まる」がありました。

TheSchwartz + WorkerManagerだと、複数のワーカーが一斉にジョブにUPDATEをかけてしまうので詰まるらしいですが、Fireworqはアーキテクチャーに複数のAPIハンドラと単一ディスパッチャを導入してこの問題を解決しているらしいです。(スライド13ページ参照)

インターフェースもTheSchwartz + WorkerManagerだとPerlのみでしか扱えなかったのですが、FireworqだとインターフェースがHTTPとなって言語の依存性がなくなったのこと。

Fireworqのスロッティングの前提として「ジョブは冪等にしておく」というのがあり、これは完全にアプリ層の責任になります。(スライド22ページ参照)

トークの内容としては、以上になるのですが、自分はこのトークを聞いてくまぎさんの「分散キューという名の苦しみ」という記事を思い出しました。

「Exactly OnceとかAtleast Once対応なんて喧伝されていることはよくあるが、仮にそのような信頼性をミドルウェアのレイヤーだけで解決しているとしたら、そのミドルウェア自体がSPoF(単一障害点)となるだろう。」(Software Transactional Memo. "分散キューという名の苦しみ". https://kumagi.hatenablog.com/entry/queue_struggle . 2023年3月22日)


「Exactly Onceな実行というのは何か特定のミドルウェアを使えば魔法のようにいつのまにか解決されるものではなく、アプリ層まで深く食い込んで設計されて初めてなし得る物である。」(Software Transactional Memo. "分散キューという名の苦しみ". https://kumagi.hatenablog.com/entry/queue_struggle . 2023年3月22日)

イージーな設計に逃げず、シンプルに全てのレイヤーで責任を持ってベストプラクティスを追い求めるのがエンジニアリング」なのだとこのトークで再認識しました。


4.4.3.1.2. 4PB(ペタバイト)を超えるオブジェクトストレージをハードウェアからアプリケーションにかけて運用するノウハウ

GMOペパボ株式会社が保有する独自のオブジェクトストレージ、Bayt(ベイト)の設計、実装、運用を通して得た知見をハードウェアからアプリケーションのレイヤーにかけて広く紹介していました。

2023/3/16時点でBaytの総容量は4.58ペタバイトらしいです。

MogileFS(Perlで実装されたスケーラブル分散ストレージを構築できるOSS)を用いたオブジェクトストレージのアーキテクチャハードウェアの運用S3 REST API互換のAPIの実装BaytからS3に移設するにいたったきっかけまで非常に面白い内容となっていました。

ハードウェアの運用についてはざっくばらんに説明します。

ハードウェアの運用にあたって、「ハード(サーバ、HDD)がいつか必ず壊れるし、壊れてからでは対応が遅い」+ 「サーバラック代は1ラック月額(20-30万円)と高いのでハードを単純増加的に増やせない」という2つの問題があります。

この2つの問題の解決策として「古いハードは破棄してラックスペースを確保し、確保したスペースに性能のいいHDD、サーバを投入する」ということをしているらしいです。サーバに関しては、流れとともに高性能なものが出現するし、HDDも年々記録密度が増え続けているらしく、空間あたりのハードの性能がスケールアップするので、「空間を増やすよりかは、空間あたりの性能を増やす」という運用がいいという話を聞きました。かなり面白かったです。

あと、ハード面の面白い話として、Baytの要件的に回転数: 7200rpm(1/120秒で一回転)の一般的なHDDの回転数で足りる、Disk I/Oがそんなにボトルネックになっていないなどの面白い話が聞けました。

オブジェクオストレージAPIの実装と、GETリクエストが来たときの処理の流れの説明なども非常に興味深かったです。

ちなみに現在、BaytはS3に移設されたとのことですが、理由としては開発当初はコスト重視をしてBaytを利用していたが、サービスの成長に伴って機能面を重視するようになったとのことでした。

移設にあたっての課題があったらしいのですが、今回のトークでは割愛されていて、ブログを参照してくださいとのことでした。

tech.pepabo.com


4.4.3.1.3. 【学生限定】スポンサー企業の方々とのランチ

学生限定のランチイベントがあったのですが、中身としてはランチを食べながらスポンサー企業の方々と軽く談笑するという内容でした。

当日無料で配布されていた弁当がこちら

開くとこのような感じ

豪華ですね...、美味しかった...。

学生と談笑する「スポンサー企業の方々」なのですが、「株式会社はてな」様からYasuhiro Onishiさん、「合同会社もりたつ技商」様からTatsuro Hisamoriさん、「合同会社 Y.pm」様からわいとんという学生相手には豪華すぎる談笑相手でした...。

談笑の内容は、事前に学生がアンケートで記入した、スポンサー企業の方々に向けた質問(趣味は?程度の軽い質問でも、技術的な真面目な質問なんでもOK)に企業の人達に答えてもらうといったものです。

また、質問は番号付きのくじになっていて、その質問を引いた人に答えてもらう + 質問の番号を使ってビンゴ大会をするといった楽しくなる工夫がされていました。

ビンゴ大会開始前

ビンゴ大会終了後

ビンゴの景品はAmazonギフトカード1000円分!! 楽しいなぁ...。

ちなみに、僕が用意した質問をわいとんさんが引いてくれて、質問の内容が「新人時代にあった失敗はどのようなものですか」というものでした。

わいとんさん 「あまりないかなぁ...。」

Yoshisaur「インシデントとか起こさなかったのでしょうか」

わいとんさん「インシデントはたくさん起こしましたよ、でも失敗とは違います」

Yoshisaur「なるほど...」

という感じの会話をしてました。僕の中でインシデント=失敗みたいな臆病な認識があったので、失敗のボーダーラインが高いエンジニアが第一線で活躍するんだろうなぁ...。という教訓を得ました。


4.4.3.1.4. ブース見学

ブースも開催されていて、自分が覚えている範囲でいうとブースを開いていた企業は「SAKURA internet」、「Mobile Factory」、「DeNA」、「HelpFeel」、「Money Forward」、「面白法人カヤック」などがありました。

中でも自分が面白いなとおもったブースはDeNAと面白法人カヤックのブースでした。

DeNAは大量のコップを無料で数無制限で配布していて、自分は6個貰いました。

DeNAのブース

DeNAのコップ

面白法人カヤックはおみくじを開催していました。嘘か真か、大吉がもらえると面白法人カヤックの書類選考が免除になるファストパスが貰えるらしいです。

面白法人カヤックのブース

Yoshisaurのおみくじの結果

4.4.4. YAPC終了後の懇親会

4.4.4.1 学生だけで集まって懇親会

TwitterのDMで学生だけで懇親会をしようとなり、焼き肉を食べました。画像は取り忘れました。苦笑

色々、勉強や就活の話をしました。

YAPCに参加する学生は、軒並みレベルの高さのレベルが異次元だったということがその懇親会でわかりました。

4.4.4.2 はてなのオフィスで集まって懇親会

学生だけの懇親会のあとはYAPC運営スタッフと一般参加者と学生参加者の懇親会が開かれました。

はてなオフィスの写真

左からかがみもちまたっくYoshisaurうーたん

はてなの詳しい話だったり、一般参加者と技術的な話ができてとても楽しかった。

Yoshisaur「ISUCONこの前出た時、チームでSQLiteからMySQLには移行しないという決断をして...」

一般参加者A 「そうだね、それも正解だね」

Yoshisaur 「MySQLに移行するとしたら、そのメリットは...」

一般参加者A 「まず、SQLiteだとネットワーク越しに他のホストのデータベースを参照できないからスケールアウトしなくて...(即答)」

Yoshisaur 「ちなみに、初参加だったのですが、ISUCON本めっちゃ良かったので少し点数稼げました。」

一般参加者B 「あーその本書くの僕手伝ったよ」

Yoshisaur 「え」

みたいな世界線でした。

いい刺激になったなぁ...。

5. YAPCの感想

「参加して本当に良かった...。」に尽きます。

楽しい、面白い、ワイワイ。

6. 謝辞

YAPC開催を支援してくださった企業様、個人スポンサー、運営スタッフの皆様、YAPCのお誘いをしてくれたアナグラ先輩、このような機会を設けていただき、本当にありがとうございました。

そして、学生支援制度を支援してくださった、株式会社はてな様、合同会社もりたつ技商様、合同会社 Y.pm様に感謝申し上げます。

学生支援制度がなければ今回YAPCに参加することはできませんでした。

7. 次のYAPC

次のYAPCYAPC::Hiroshimaになるらしい。学生のうちに行く機会があれば絶対に行きたいと思います。

コンパイラ構成論

最終的な成績

Bでした

課題に関するファイル

課題に関するファイルはGitHubに置かれている

課題の提出方法に関する注意点

この講義ページにて、

この授業中の問題は、
    Subject: Report on Compiler consturction Lecture Exercise 1.1

などというSubjectのメールにして、kono@ie.u-ryukyu.ac.jp まで送ること。

と書かれているが、consturctionがスペルミスで、スペルミスを訂正した状態、訂正しない状態で提出すればいいかわからない

ちなみに、著者はスペル訂正なし、スペル訂正ありの両方のバージョンのメールを送っている

1.1 LLVM

この問題を解く

sample.cという名前の、以下のような内容のファイルを作成した

extern int printf(const char *,...);
#define TYPE int
TYPE f(TYPE a, TYPE b) {
    return a + b;
}
int main() 
{
    TYPE a = 1;
    TYPE b = 2;
    printf("%x = %x + %x \n",f(a,b),a,b);
    return 0;
}

(1) cpp

   clang -E

での出力を調べる。
変換されている部分はどこか。 printf を protoptype ではなく #include <stdio.h> で定義した時はどうなるか。

$ clang -E sample.cを実行しあ

# 1 "sample.c"
# 1 "<built-in>" 1
# 1 "<built-in>" 3
# 368 "<built-in>" 3
# 1 "<command line>" 1
# 1 "<built-in>" 2
# 1 "sample.c" 2
extern int printf(const char *,...);

int f(int a, int b) {
    return a + b;
}
int main()
{
    int a = 1;
    int b = 2;
    printf("%x = %x + %x \n",f(a,b),a,b);
    return 0;
}
  • #define TYPE intによって「TYPE」が「int」にマクロ展開されている
  • インクルードファイルが展開されている

sample2.cというファイル名で中身が以下のファイルを用意した

printf を protoptype ではなく #include <stdio.h> で定義したファイルである

#include <stdio.h>
#define TYPE int
TYPE f(TYPE a, TYPE b) {
    return a + b;
}
int main() 
{
    TYPE a = 1;
    TYPE b = 2;
    printf("%x = %x + %x \n",f(a,b),a,b);
    return 0;
}

$ clang -E sample2.cを実行した

出力は、長すぎたので割愛

#include <stdio.h>でstdio.hというヘッダファイルがインクルードされ、そこに含まれるprintfなどの標準入出力関数のプロトタイプ宣言によって、「printf」関数に関する詳細な情報が展開されているのが原因


(2) アセンブラ

   clang -S  -O0

で出力されるアセンブラについて調べる。
   clang -S  -O

についても調べる。
関数f はどうなっているか。

$ clang -S -O0 sample.c

アセンブラを出力し、sample.sというファイルに書き込む

 .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 12, 0 sdk_version 12, 0
    .globl  _f                              ## -- Begin function f
    .p2align    4, 0x90
_f:                                     ## @f
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -4(%rbp), %eax
    addl    -8(%rbp), %eax
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .globl  _main                           ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp
    movl    $0, -4(%rbp)
    movl    $1, -8(%rbp)
    movl    $2, -12(%rbp)
    movl    -8(%rbp), %edi
    movl    -12(%rbp), %esi
    callq   _f
    movl    %eax, %esi
    movl    -8(%rbp), %edx
    movl    -12(%rbp), %ecx
    leaq    L_.str(%rip), %rdi
    movb    $0, %al
    callq   _printf
    xorl    %eax, %eax
    addq    $16, %rsp
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz  "%x = %x + %x \n"

.subsections_via_symbols

C言語ソースコードsample.cがアセンブラ言語(x86_64アセンブラ)に変換されている。

f関数は、レジスタ%ediと%esiから引数aとbを取り出し、それらをメモリアドレス-4(%rbp)と-8(%rbp)に保存する。次に、-4(%rbp)と-8(%rbp)を加算し、その結果をレジスタ%eaxに格納する。最後に、%eaxに格納された値を返すために、retqで関数からのリターンを行う。

$ mv sample.s sample.O0.sでsample.sをsample.O0.sというファイル名にする

$ clang -S -O sample.cで最適化レベルを1にしている

O0は最適化レベルを0(最小)に設定し、最適化を行わないが、このため、生成されたアセンブラコードは実行速度が遅い可能性がある、しかし、デバッグトラブルシューティングに役立つことがある

Oは最適化レベルを1に設定し、最適化を行う。このため、生成されたアセンブラコードは実行速度が速い可能性があるが、デバッグトラブルシューティングには不向きである

$ mv sample.s sample.O.sを実行し、sample.O0.sとファイル名を区別できるようにした

sample.O.sの中身は以下のようになっている

 .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 12, 0 sdk_version 12, 0
    .globl  _f                              ## -- Begin function f
    .p2align    4, 0x90
_f:                                     ## @f
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
                                        ## kill: def $esi killed $esi def $rsi
                                        ## kill: def $edi killed $edi def $rdi
    leal    (%rdi,%rsi), %eax
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .globl  _main                           ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    movl    $1, %edi
    movl    $2, %esi
    callq   _f
    leaq    L_.str(%rip), %rdi
    movl    %eax, %esi
    movl    $1, %edx
    movl    $2, %ecx
    xorl    %eax, %eax
    callq   _printf
    xorl    %eax, %eax
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz  "%x = %x + %x \n"

.subsections_via_symbols

-Oオプションで最適化したアセンブラの関数fも-O0オプションで最適化したアセンブラの関数fも実装は変わらない

(3) LLVM byte code

   clang  -emit-llvm -S 

LLVM バイトコードの出力が得られることを確認せよ。
アセンブラとの対応を示せ。

$ clang -emit-llvm -S sample.cLLVM バイトコードをsample.llに出力した

出力は以下のようなもの

; ModuleID = 'sample.c'
source_filename = "sample.c"
target datalayout = "e-m:o-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-apple-macosx12.0.0"

@.str = private unnamed_addr constant [15 x i8] c"%x = %x + %x \0A\00", align 1

; Function Attrs: noinline nounwind optnone ssp uwtable
define i32 @f(i32 %0, i32 %1) #0 {
  %3 = alloca i32, align 4
  %4 = alloca i32, align 4
  store i32 %0, i32* %3, align 4
  store i32 %1, i32* %4, align 4
  %5 = load i32, i32* %3, align 4
  %6 = load i32, i32* %4, align 4
  %7 = add nsw i32 %5, %6
  ret i32 %7
}

; Function Attrs: noinline nounwind optnone ssp uwtable
define i32 @main() #0 {
  %1 = alloca i32, align 4
  %2 = alloca i32, align 4
  %3 = alloca i32, align 4
  store i32 0, i32* %1, align 4
  store i32 1, i32* %2, align 4
  store i32 2, i32* %3, align 4
  %4 = load i32, i32* %2, align 4
  %5 = load i32, i32* %3, align 4
  %6 = call i32 @f(i32 %4, i32 %5)
  %7 = load i32, i32* %2, align 4
  %8 = load i32, i32* %3, align 4
  %9 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([15 x i8], [15 x i8]* @.str, i64 0, i64 0), i32 %6, i32 %7, i32 %8)
  ret i32 0
}

declare i32 @printf(i8*, ...) #1

attributes #0 = { noinline nounwind optnone ssp uwtable "darwin-stkchk-strong-link" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "probe-stack"="___chkstk_darwin" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { "darwin-stkchk-strong-link" "disable-tail-calls"="false" "frame-pointer"="all" "less-precise-fpmad"="false" "no-infs-fp-math"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="true" "probe-stack"="___chkstk_darwin" "stack-protector-buffer-size"="8" "target-cpu"="penryn" "target-features"="+cx16,+cx8,+fxsr,+mmx,+sahf,+sse,+sse2,+sse3,+sse4.1,+ssse3,+x87" "tune-cpu"="generic" "unsafe-fp-math"="false" "use-soft-float"="false" }

!llvm.module.flags = !{!0, !1, !2}
!llvm.ident = !{!3}

!0 = !{i32 2, !"SDK Version", [2 x i32] [i32 12, i32 0]}
!1 = !{i32 1, !"wchar_size", i32 4}
!2 = !{i32 7, !"PIC Level", i32 2}
!3 = !{!"Apple clang version 13.0.0 (clang-1300.0.29.3)"}

関数fの実装に限定して、アセンブラLLVMバイトコードの対応を表にして説明する

アセンブラ LLVMバイトコード 説明
_f: define i32 @f(i32 %0, i32 %1) #0 { 関数fの定義
pushq %rbp %3 = alloca i32, align 4 フレームポインタを退避
movq %rsp, %rbp %4 = alloca i32, align 4 フレームポインタをセット
movl %edi, -4(%rbp) store i32 %0, i32* %3, align 4 第一引数を格納
movl %esi, -8(%rbp) store i32 %1, i32* %4, align 4 第二引数を格納
movl -4(%rbp), %eax %5 = load i32, i32* %3, align 4 第一引数をレジスタにロード
addl -8(%rbp), %eax %6 = load i32, i32* %4, align 4 第二引数をレジスタにロード
popq %rbp %7 = add nsw i32 %5, %6 加算実行
retq ret i32 %7 関数からの返却
.cfi_endproc } 関数の終了

(4) a.out

出力される a.out を otool を使って調べる。

$ clang sample.cを実行して、a.outというファイルを出力した

$ otool -tv a.outを実行した

a.out:
(__TEXT,__text) section
_f:
0000000100003f20        pushq   %rbp
0000000100003f21        movq    %rsp, %rbp
0000000100003f24        movl    %edi, -0x4(%rbp)
0000000100003f27        movl    %esi, -0x8(%rbp)
0000000100003f2a        movl    -0x4(%rbp), %eax
0000000100003f2d        addl    -0x8(%rbp), %eax
0000000100003f30        popq    %rbp
0000000100003f31        retq
0000000100003f32        nopw    %cs:(%rax,%rax)
0000000100003f3c        nopl    (%rax)
_main:
0000000100003f40        pushq   %rbp
0000000100003f41        movq    %rsp, %rbp
0000000100003f44        subq    $0x10, %rsp
0000000100003f48        movl    $0x0, -0x4(%rbp)
0000000100003f4f        movl    $0x1, -0x8(%rbp)
0000000100003f56        movl    $0x2, -0xc(%rbp)
0000000100003f5d        movl    -0x8(%rbp), %edi
0000000100003f60        movl    -0xc(%rbp), %esi
0000000100003f63        callq   0x100003f20
0000000100003f68        movl    %eax, %esi
0000000100003f6a        movl    -0x8(%rbp), %edx
0000000100003f6d        movl    -0xc(%rbp), %ecx
0000000100003f70        leaq    0x2f(%rip), %rdi
0000000100003f77        movb    $0x0, %al
0000000100003f79        callq   0x100003f86
0000000100003f7e        xorl    %eax, %eax
0000000100003f80        addq    $0x10, %rsp
0000000100003f84        popq    %rbp
0000000100003f85        retq

各命令のメモリアドレスと対応するアセンブラが一緒に出力される

ARMアセンブラ

  -arch arm を付けて、ARMのアセンブラの出力を調べよ
  -arch i386 
  -arch x86_64 
   clang -print-targets

arm

$ clang -arch arm -S sample.cを実行し、armのアセンブラをsample.sに出力する $ $ mv sample.s sample.arm.sを実行して、ファイル名を変更する

sample.arm.sの中身

 .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 12, 0 sdk_version 12, 0
    .syntax unified
    .globl  _f                              @ -- Begin function f
    .p2align    2
    .code   32                              @ @f
_f:
@ %bb.0:
    sub sp, sp, #8
    str r0, [sp, #4]
    str r1, [sp]
    ldr r0, [sp, #4]
    ldr r1, [sp]
    add r0, r0, r1
    add sp, sp, #8
    bx  lr
                                        @ -- End function
    .globl  _main                           @ -- Begin function main
    .p2align    2
    .code   32                              @ @main
_main:
@ %bb.0:
    push    {r7, lr}
    mov r7, sp
    sub sp, sp, #16
    mov r0, #0
    str r0, [sp]                        @ 4-byte Spill
    str r0, [r7, #-4]
    mov r0, #1
    str r0, [sp, #8]
    mov r0, #2
    str r0, [sp, #4]
    ldr r0, [sp, #8]
    ldr r1, [sp, #4]
    bl  _f
    mov r1, r0
    ldr r2, [sp, #8]
    ldr r3, [sp, #4]
    ldr r0, LCPI1_0
LPC1_0:
    add r0, pc, r0
    bl  _printf
                                        @ kill: def $r1 killed $r0
    ldr r0, [sp]                        @ 4-byte Reload
    mov sp, r7
    pop {r7, lr}
    bx  lr
    .p2align    2
@ %bb.1:
    .data_region
LCPI1_0:
    .long   L_.str-(LPC1_0+8)
    .end_data_region
                                        @ -- End function
    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 @ @.str
    .asciz  "%x = %x + %x \n"

.subsections_via_symbols

i386

$ clang -arch i386 -S sample.cを実行し、i386アセンブラをsample.sに出力する $ $ mv sample.s sample.i386.sを実行して、ファイル名を変更する

sample.i386.sの中身

 .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 12, 0 sdk_version 12, 0
    .globl  _f                              ## -- Begin function f
    .p2align    4, 0x90
_f:                                     ## @f
    .cfi_startproc
## %bb.0:
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset %ebp, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register %ebp
    movl    12(%ebp), %eax
    movl    8(%ebp), %eax
    movl    8(%ebp), %eax
    addl    12(%ebp), %eax
    popl    %ebp
    retl
    .cfi_endproc
                                        ## -- End function
    .globl  _main                           ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset %ebp, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register %ebp
    pushl   %esi
    subl    $36, %esp
    .cfi_offset %esi, -12
    calll   L1$pb
L1$pb:
    popl    %eax
    movl    %eax, -20(%ebp)                 ## 4-byte Spill
    movl    $0, -8(%ebp)
    movl    $1, -12(%ebp)
    movl    $2, -16(%ebp)
    movl    -12(%ebp), %ecx
    movl    -16(%ebp), %eax
    movl    %ecx, (%esp)
    movl    %eax, 4(%esp)
    calll   _f
    movl    %eax, %edx
    movl    -20(%ebp), %eax                 ## 4-byte Reload
    leal    L_.str-L1$pb(%eax), %esi
    movl    -12(%ebp), %ecx
    movl    -16(%ebp), %eax
    movl    %esi, (%esp)
    movl    %edx, 4(%esp)
    movl    %ecx, 8(%esp)
    movl    %eax, 12(%esp)
    calll   _printf
    xorl    %eax, %eax
    addl    $36, %esp
    popl    %esi
    popl    %ebp
    retl
    .cfi_endproc
                                        ## -- End function
    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz  "%x = %x + %x \n"

.subsections_via_symbols

x86_64

$ clang -arch x86_64 -S sample.cを実行し、x86_64のアセンブラをsample.sに出力する $ $ mv sample.s sample.x86_64.sを実行して、ファイル名を変更する

sample.x86_64.sの中身

 .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 12, 0 sdk_version 12, 0
    .globl  _f                              ## -- Begin function f
    .p2align    4, 0x90
_f:                                     ## @f
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    movl    %edi, -4(%rbp)
    movl    %esi, -8(%rbp)
    movl    -4(%rbp), %eax
    addl    -8(%rbp), %eax
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .globl  _main                           ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp
    movl    $0, -4(%rbp)
    movl    $1, -8(%rbp)
    movl    $2, -12(%rbp)
    movl    -8(%rbp), %edi
    movl    -12(%rbp), %esi
    callq   _f
    movl    %eax, %esi
    movl    -8(%rbp), %edx
    movl    -12(%rbp), %ecx
    leaq    L_.str(%rip), %rdi
    movb    $0, %al
    callq   _printf
    xorl    %eax, %eax
    addq    $16, %rsp
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz  "%x = %x + %x \n"

.subsections_via_symbols

clang -print-targets

$ clang -print-targetsを実行するとclangコンパイラにサポートされているターゲットアーキテクチャのリストが出力される

  Registered Targets:
    aarch64    - AArch64 (little endian)
    aarch64_32 - AArch64 (little endian ILP32)
    aarch64_be - AArch64 (big endian)
    arm        - ARM
    arm64      - ARM64 (little endian)
    arm64_32   - ARM64 (little endian ILP32)
    armeb      - ARM (big endian)
    thumb      - Thumb
    thumbeb    - Thumb (big endian)
    x86        - 32-bit X86: Pentium-Pro and above
    x86-64     - 64-bit X86: EM64T and AMD64

2.1

この問題を解く

以下のprogram check_endian.c がある。
    int check = 0x12345678;
    main()
    {
    char i, *ptr;
    
    ptr = (char *)&check; 
    i = ptr[1];
    return i;
    }

このprogramをcompileしたassemblerを、i386, emt64 のCPUで表示させて見よ。また、gdb で i にどのような値が入るかを確認せよ。そのCPUは、Little-Endian か Big-Endian かを答えよ。また、 trace の結果を、確認せよ。
Endian の変換はどのような時に必要になるか。どのようにすれば実現できるか?

Unix には、Builtin のEndianの変換関数がある。それを探し出せ。また、その実装がどうなっているかを調べよ。(ヒント: man -k を使う)

program check_endian.cというファイル名で中身が以下のようなファイルを作る

int check = 0x12345678;

int main()
{
    char i, *ptr;
    ptr = (char *)&check;
    i = ptr[1];
    return i;
}

compileしたassemblerを、i386, emt64 のCPUで表示

$ clang -arch i386 -S check_endian.c && mv check_endian.s check_endian.i386.sを実行して、check_endian.i386.sというファイルにi386アセンブラを出力する

check_endian.i386.sの中身

 .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 12, 0 sdk_version 12, 0
    .globl  _main                           ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushl   %ebp
    .cfi_def_cfa_offset 8
    .cfi_offset %ebp, -8
    movl    %esp, %ebp
    .cfi_def_cfa_register %ebp
    subl    $12, %esp
    calll   L0$pb
L0$pb:
    popl    %eax
    leal    _check-L0$pb(%eax), %eax
    movl    $0, -4(%ebp)
    movl    %eax, -12(%ebp)
    movl    -12(%ebp), %eax
    movb    1(%eax), %al
    movb    %al, -5(%ebp)
    movsbl  -5(%ebp), %eax
    addl    $12, %esp
    popl    %ebp
    retl
    .cfi_endproc
                                        ## -- End function
    .section    __DATA,__data
    .globl  _check                          ## @check
    .p2align    2
_check:
    .long   305419896                       ## 0x12345678

.subsections_via_symbols

$ clang -arch x86_64 -S check_endian.c && mv check_endian.s check_endian.x86_64.sを実行して、check_endian.x86_64.sというファイルにemt64のアセンブラを出力する

check_endian.x86_64.sの中身

 .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 12, 0 sdk_version 12, 0
    .globl  _main                           ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    leaq    _check(%rip), %rax
    movl    $0, -4(%rbp)
    movq    %rax, -16(%rbp)
    movq    -16(%rbp), %rax
    movb    1(%rax), %al
    movb    %al, -5(%rbp)
    movsbl  -5(%rbp), %eax
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .section    __DATA,__data
    .globl  _check                          ## @check
    .p2align    2
_check:
    .long   305419896                       ## 0x12345678

.subsections_via_symbols

gdb で i にどのような値が入るかを確認

このようなファイル構成を作るためにファイルを移動、作成する

.
├── c_env
│   ├── Dockerfile
│   ├── check_endian.c
│   ├── check_endian.i386.s
│   └── check_endian.x86_64.s
└── docker-compose.yml

docker-comopse.yml

services:
  c_env:
    build: ./c_env/
    container_name: c_env
    security_opt:
      - seccomp:unconfined
    volumes:
      - "./c_env:/workplace"
    tty: true

c_env/Dockerfile

FROM ubuntu:22.04
RUN mkdir workplace &&\
    apt-get update &&\
    apt-get -y install lldb gdb cmake manpages-dev
WORKDIR /workplace/

Docker Desktopを起動する

$ docker-compose up -d

$ docker-compose exec c_env bash

$ gcc -m64 -g -O0 -o check_endian check_endian.cアーキテクチャx86_64で指定して、コンパイルする

$ gdb check_endianを実行した

(gdb) break check_endian.c:8で8行目にbreakpointを設置

(gdb) runで実行した

(gdb) info locals iを実行したら、i = 86 'V'と出力された。

CPUは、Little-Endian か Big-Endian

(gdb) x/20b &checkで「check」変数に格納されている値を20バイト分のバイナリ値として表示した

0x555555558010 <check>: 120     86      52      18      0       0       0       0
0x555555558018: 0       0       0       0       0       0       0       0
0x555555558020: 0       0       0       0

最初の8バイト目(0x555555558010)に格納されている値は、 120 86 52 18 0 0 0 0だった

little-endianでは、低いメモリアドレスに格納されるバイトが先頭バイトになるため、0x555555558010のように0が最後のバイトに格納される

trace の結果

(gdb) exitで一回抜ける gdb check_endianでもう一度gdbに入る

(gdb) tbreak main トレースポイント設置 (gdb) run 実行 (gdb) backtraceで、関数の呼び出し順や、どの関数からどの関数へと呼び出されたのかなどの現在のトレースポイントまでのトレース情報を一覧表示している

#0  main () at check_endian.c:6

Endian の変換

Endian の変換はどのような時に必要になるか。どのようにすれば実現できるか?

Endian変換は、バイトオーダーが異なるシステム間でのデータのやりとりや、データの保存・読み込みなどで必要になる

例: ネットワークでバイナリを送る場合

C言語では、「htons」や「htonl」などの関数を使ってEndian変換を実現できる。

コマンドでは、「xxd」や「od」などのコマンドを使って、ファイルの内容を16進数形式で表示し、手作業でEndian変換を行うこともできる。

UNIXのbuiltinのEndianの変換関数の場所と実装

endian_conversion.cというファイル名で以下のような内容のファイルを用意する

#include <stdio.h>
#include <arpa/inet.h>

int main() {
    unsigned short x = 12345;
    unsigned short y = htons(x);

    printf("x = %d\n", x);
    printf("y = %d\n", y);

    return 0;
}

$ clang -E endian_conversion.c | grep arpa/inetを実行

# 1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/arpa/inet.h" 1 3 4
# 68 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/arpa/inet.h" 3 4
# 69 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/arpa/inet.h" 2 3 4
# 70 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/arpa/inet.h" 2 3 4
# 72 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/arpa/inet.h" 2 3 4

/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/arpa/inet.hを見る

htonsの実装がない、代わりに#include <machine/endian.h>という行を見つけた

/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/machine/endian.hを見る

まだ、htonsの実装がない、代わりに#include "i386/endian.h"という行を見つけた

/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/i386/endian.hを見る

まだ、htonsの実装がない、代わりに#include <sys/_endian.h>という行を見つけた

/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/sys/_endian.hを見る

#if defined(lint)

__BEGIN_DECLS
__uint16_t      ntohs(__uint16_t);
__uint16_t      htons(__uint16_t);
__uint32_t      ntohl(__uint32_t);
__uint32_t      htonl(__uint32_t);
__END_DECLS

このようにhtonsがマクロとして定義されていた


3.1 intel64 (emt64) のアセンブラ

この問題を解く

3.1の課題ページのurlがhttps://ie.u-ryukyu.ac.jp/~kono/lecture/compiler/ex/005になっているが、https://ie.u-ryukyu.ac.jp/~kono/lecture/compiler/ex/005.htmlに修正したら、見れた。メールを出すときにもその旨を伝えておくといいかもしれない。

課題ページにあるtest1.cをダウンロードするか、自前でtest1.cを用意する

extern int printf(const char *,...);

unsigned char a[] = {
      0x01,
      0x02,
      0x03,
      0x04,
      0x05,
      0x06,
      0x07,
      0x08,
      0x55,
      0x12,
};

long
test(unsigned char *a, long j)
{
   return j;
}

int
main() 
{
    long x = 0;
    x = test(a,x);
    printf("x = %lx\n", x);
    return 0;
}

test1.sと a.outを用意する

$ clang -S test1.cアセンブラを出力しtest1.sに書き込む

$ clang test1.sでa.outを用意する

CPU の endian を x/20x と x/20b を使って確認する

$ lldb a.out

(lldb) b test

(lldb) run

Process 52594 launched: 'a.out' (x86_64)
Process 52594 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003f20 a.out`test
a.out`test:
->  0x100003f20 <+0>: pushq  %rbp
    0x100003f21 <+1>: movq   %rsp, %rbp
    0x100003f24 <+4>: movq   %rdi, -0x8(%rbp)
    0x100003f28 <+8>: movq   %rsi, -0x10(%rbp)
Target 0: (a.out) stopped.

(lldb) x/20x &a

0x100008010: 0x04030201 0x08070605 0x00001255 0x00000000
0x100008020: 0x00000000 0x00000000 0x00000000 0x00000000
0x100008030: 0x00000000 0x00000000 0x00000000 0x00000000
0x100008040: 0x00000000 0x00000000 0x00000000 0x00000000
0x100008050: 0x00000000 0x00000000 0x00000000 0x00000000

(lldb) x/20b &a

0x100008010: 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08
0x100008018: 0x55 0x12 0x00 0x00 0x00 0x00 0x00 0x00
0x100008020: 0x00 0x00 0x00 0x00

レジスタの値を調べる

(lldb) p $rdi
(unsigned long) $2 = 4295000080
(lldb) p $rsi
(unsigned long) $3 = 0

lldbからログアウトする(ctrl-d)

test1.s を書き換えて、さまざまなアセンブラ命令を試す

書き換えずにコピーを用意してアセンブラ命令を出す

  • $ cp test1.s test2.sでtest2.sの中身をコピーしたtest2.sを作成した

以下のように編集した

test2.s

 .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 12, 0 sdk_version 12, 0
    .globl  _test                           ## -- Begin function test
    .p2align    4, 0x90
_test:                                  ## @test
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    movq    %rdi, -8(%rbp)
    movq    %rsi, -16(%rbp)
    movq    (%rdi),%rax
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .globl  _main                           ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp
    movl    $0, -4(%rbp)
    movq    $0, -16(%rbp)
    movq    -16(%rbp), %rsi
    leaq    _a(%rip), %rdi
    callq   _test
    movq    %rax, -16(%rbp)
    movq    -16(%rbp), %rsi
    leaq    L_.str(%rip), %rdi
    movb    $0, %al
    callq   _printf
    xorl    %eax, %eax
    addq    $16, %rsp
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .section    __DATA,__data
    .globl  _a                              ## @a
_a:
    .ascii  "\001\002\003\004\005\006\007\bU\022"

    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz  "x = %lx\n"

.subsections_via_symbols

test1.sとtest2.sの差分、上がtest1.sで下がtest2.s

13,16c13,16
<    movq    %rdi, -8(%rbp)
<    movq    %rsi, -16(%rbp)
<    movq    -16(%rbp), %rax
<    popq    %rbp
---
>    movq    %rdi, -8(%rbp)
>    movq    %rsi, -16(%rbp)
>    movq    (%rdi),%rax
>    popq    %rbp

$ mv a.out a.1.outでa.outのtest1.sバージョンのバックアップを取った

$ clang test2.sでa.outを生成した

lldbで以下のような作業をした

$ lldb a.out
(lldb) target create "a.out"
Current executable set to 'a.out' (x86_64).
(lldb) b test
Breakpoint 1: where = a.out`test, address = 0x0000000100003f20
(lldb) process launch
Process 53418 launched: 'a.out' (x86_64)
Process 53418 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003f20 a.out`test
a.out`test:
->  0x100003f20 <+0>: pushq  %rbp
    0x100003f21 <+1>: movq   %rsp, %rbp
    0x100003f24 <+4>: movq   %rdi, -0x8(%rbp)
    0x100003f28 <+8>: movq   %rsi, -0x10(%rbp)
Target 0: (a.out) stopped.
(lldb) stepi
Process 53418 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f21 a.out`test + 1
a.out`test:
->  0x100003f21 <+1>:  movq   %rsp, %rbp
    0x100003f24 <+4>:  movq   %rdi, -0x8(%rbp)
    0x100003f28 <+8>:  movq   %rsi, -0x10(%rbp)
    0x100003f2c <+12>: movq   (%rdi), %rax
Target 0: (a.out) stopped.
(lldb) stepi
Process 53418 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f24 a.out`test + 4
a.out`test:
->  0x100003f24 <+4>:  movq   %rdi, -0x8(%rbp)
    0x100003f28 <+8>:  movq   %rsi, -0x10(%rbp)
    0x100003f2c <+12>: movq   (%rdi), %rax
    0x100003f2f <+15>: popq   %rbp
Target 0: (a.out) stopped.
(lldb) stepi
Process 53418 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f28 a.out`test + 8
a.out`test:
->  0x100003f28 <+8>:  movq   %rsi, -0x10(%rbp)
    0x100003f2c <+12>: movq   (%rdi), %rax
    0x100003f2f <+15>: popq   %rbp
    0x100003f30 <+16>: retq
Target 0: (a.out) stopped.
(lldb) stepi
Process 53418 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f2c a.out`test + 12
a.out`test:
->  0x100003f2c <+12>: movq   (%rdi), %rax
    0x100003f2f <+15>: popq   %rbp
    0x100003f30 <+16>: retq
    0x100003f31 <+17>: nopw   %cs:(%rax,%rax)
Target 0: (a.out) stopped.
(lldb) stepi
Process 53418 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f2f a.out`test + 15
a.out`test:
->  0x100003f2f <+15>: popq   %rbp
    0x100003f30 <+16>: retq
    0x100003f31 <+17>: nopw   %cs:(%rax,%rax)
    0x100003f3b <+27>: nopl   (%rax,%rax)
Target 0: (a.out) stopped.
(lldb) p (void*) $rax
(void *) $0 = 0x0807060504030201
(lldb) p (void*) $ah
(void *) $1 = 0x0000000000000002
(lldb) p (void*) $al
(void *) $2 = 0x0000000000000001
(lldb) p (void*) $eax
(void *) $3 = 0x0000000004030201
(lldb) p (void*) $rbx
(void *) $4 = 0x00000001000c0060

作業のログをもとにmovq (%rdi),%raxを実行した時点の各レジスタの値を表にした。

レジスタ 格納されている値
%rax 0x0807060504030201
%ah 0x0000000000000002
%al 0x0000000000000001
%eax 0x0000000004030201
%rbx 0x00000001000c0060

test1.s を書き換えて、さまざまなアセンブラ命令を試す(2)

  • $ cp test1.s test3.sでtest3.sの中身をコピーしたtest3.sを作成した

以下のように編集した

test3.s

 .section    __TEXT,__text,regular,pure_instructions
    .build_version macos, 12, 0 sdk_version 12, 0
    .globl  _test                           ## -- Begin function test
    .p2align    4, 0x90
_test:                                  ## @test
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    (%rdi), %rax
    movq    2(%rdi), %rax
    movq    2(%rdi), %rbx
    movq   %rdi, %rbx
    addq    $0x4, %rbx
    movl    (%rbx), %eax
    incq    %rbx
    movq    (%rbx),%rax
    movq    $0x4, %rax
    movl    4(%rbx,%rax),%ecx
    leaq    4(%rbx,%rax),%rcx
    movb    2(%rbx),%al
    movsbl  2(%rbx),%eax
    movsbl  3(%rbx),%eax
    movsbq  3(%rbx),%rax
    leaq    8(%rbx,%rax),%rcx
    leaq    8(%rbx,%rax,2),%rcx
    ret
    .cfi_endproc
                                        ## -- End function
    .globl  _main                           ## -- Begin function main
    .p2align    4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq    $16, %rsp
    movl    $0, -4(%rbp)
    movq    $0, -16(%rbp)
    movq    -16(%rbp), %rsi
    leaq    _a(%rip), %rdi
    callq   _test
    movq    %rax, -16(%rbp)
    movq    -16(%rbp), %rsi
    leaq    L_.str(%rip), %rdi
    movb    $0, %al
    callq   _printf
    xorl    %eax, %eax
    addq    $16, %rsp
    popq    %rbp
    retq
    .cfi_endproc
                                        ## -- End function
    .section    __DATA,__data
    .globl  _a                              ## @a
_a:
    .ascii  "\001\002\003\004\005\006\007\bU\022"

    .section    __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .asciz  "x = %lx\n"

.subsections_via_symbols

test1.sとtest3.sの差分、上がtest1.sで下がtest3.s

11,17c11,28
<    movq    %rsp, %rbp
<    .cfi_def_cfa_register %rbp
<    movq    %rdi, -8(%rbp)
<    movq    %rsi, -16(%rbp)
<    movq    -16(%rbp), %rax
<    popq    %rbp
<    retq
---
>    movq    (%rdi), %rax
>    movq    2(%rdi), %rax
>    movq    2(%rdi), %rbx
>    movq   %rdi, %rbx
>    addq    $0x4, %rbx
>    movl    (%rbx), %eax
>    incq    %rbx
>    movq    (%rbx),%rax
>    movq    $0x4, %rax
>    movl    4(%rbx,%rax),%ecx
>    leaq    4(%rbx,%rax),%rcx
>    movb    2(%rbx),%al
>    movsbl  2(%rbx),%eax
>    movsbl  3(%rbx),%eax
>    movsbq  3(%rbx),%rax
>    leaq    8(%rbx,%rax),%rcx
>    leaq    8(%rbx,%rax,2),%rcx
>    ret

$ mv a.out a.2.outでa.outのtest2.sバージョンのバックアップを取った

$ clang test3.sでa.outを生成した

lldbで以下のような作業をした

$ lldb a.out
(lldb) target create "a.out"
Current executable set to 'a.out' (x86_64).
(lldb) b test
Breakpoint 1: where = a.out`test, address = 0x0000000100003ef0
(lldb) process launch
Process 53662 launched: 'a.out' (x86_64)
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = breakpoint 1.1
    frame #0: 0x0000000100003ef0 a.out`test
a.out`test:
->  0x100003ef0 <+0>: pushq  %rbp
    0x100003ef1 <+1>: movq   (%rdi), %rax
    0x100003ef4 <+4>: movq   0x2(%rdi), %rax
    0x100003ef8 <+8>: movq   0x2(%rdi), %rbx
Target 0: (a.out) stopped.
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003ef1 a.out`test + 1
a.out`test:
->  0x100003ef1 <+1>:  movq   (%rdi), %rax
    0x100003ef4 <+4>:  movq   0x2(%rdi), %rax
    0x100003ef8 <+8>:  movq   0x2(%rdi), %rbx
    0x100003efc <+12>: movq   %rdi, %rbx
Target 0: (a.out) stopped.
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003ef4 a.out`test + 4
a.out`test:
->  0x100003ef4 <+4>:  movq   0x2(%rdi), %rax
    0x100003ef8 <+8>:  movq   0x2(%rdi), %rbx
    0x100003efc <+12>: movq   %rdi, %rbx
    0x100003eff <+15>: addq   $0x4, %rbx
Target 0: (a.out) stopped.
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003ef8 a.out`test + 8
a.out`test:
->  0x100003ef8 <+8>:  movq   0x2(%rdi), %rbx
    0x100003efc <+12>: movq   %rdi, %rbx
    0x100003eff <+15>: addq   $0x4, %rbx
    0x100003f03 <+19>: movl   (%rbx), %eax
Target 0: (a.out) stopped.
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003efc a.out`test + 12
a.out`test:
->  0x100003efc <+12>: movq   %rdi, %rbx
    0x100003eff <+15>: addq   $0x4, %rbx
    0x100003f03 <+19>: movl   (%rbx), %eax
    0x100003f05 <+21>: incq   %rbx
Target 0: (a.out) stopped.
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003eff a.out`test + 15
a.out`test:
->  0x100003eff <+15>: addq   $0x4, %rbx
    0x100003f03 <+19>: movl   (%rbx), %eax
    0x100003f05 <+21>: incq   %rbx
    0x100003f08 <+24>: movq   (%rbx), %rax
Target 0: (a.out) stopped.
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f03 a.out`test + 19
a.out`test:
->  0x100003f03 <+19>: movl   (%rbx), %eax
    0x100003f05 <+21>: incq   %rbx
    0x100003f08 <+24>: movq   (%rbx), %rax
    0x100003f0b <+27>: movq   $0x4, %rax
Target 0: (a.out) stopped.
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f05 a.out`test + 21
a.out`test:
->  0x100003f05 <+21>: incq   %rbx
    0x100003f08 <+24>: movq   (%rbx), %rax
    0x100003f0b <+27>: movq   $0x4, %rax
    0x100003f12 <+34>: movl   0x4(%rbx,%rax), %ecx
Target 0: (a.out) stopped.
(lldb) p (void*) $eax
(void *) $0 = 0x0000000008070605
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f08 a.out`test + 24
a.out`test:
->  0x100003f08 <+24>: movq   (%rbx), %rax
    0x100003f0b <+27>: movq   $0x4, %rax
    0x100003f12 <+34>: movl   0x4(%rbx,%rax), %ecx
    0x100003f16 <+38>: leaq   0x4(%rbx,%rax), %rcx
Target 0: (a.out) stopped.
(lldb) p (void*) $rbx
(void *) $0 = 0x0000000100008015
(lldb) p (void*) $rax
(void *) $1 = 0x0000000008070605
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f0b a.out`test + 27
a.out`test:
->  0x100003f0b <+27>: movq   $0x4, %rax
    0x100003f12 <+34>: movl   0x4(%rbx,%rax), %ecx
    0x100003f16 <+38>: leaq   0x4(%rbx,%rax), %rcx
    0x100003f1b <+43>: movb   0x2(%rbx), %al
Target 0: (a.out) stopped.
(lldb) p (void*) $rax
(void *) $1 = 0x0000001255080706
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f12 a.out`test + 34
a.out`test:
->  0x100003f12 <+34>: movl   0x4(%rbx,%rax), %ecx
    0x100003f16 <+38>: leaq   0x4(%rbx,%rax), %rcx
    0x100003f1b <+43>: movb   0x2(%rbx), %al
    0x100003f1e <+46>: movsbl 0x2(%rbx), %eax
Target 0: (a.out) stopped.
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f16 a.out`test + 38
a.out`test:
->  0x100003f16 <+38>: leaq   0x4(%rbx,%rax), %rcx
    0x100003f1b <+43>: movb   0x2(%rbx), %al
    0x100003f1e <+46>: movsbl 0x2(%rbx), %eax
    0x100003f22 <+50>: movsbl 0x3(%rbx), %eax
Target 0: (a.out) stopped.
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f1b a.out`test + 43
a.out`test:
->  0x100003f1b <+43>: movb   0x2(%rbx), %al
    0x100003f1e <+46>: movsbl 0x2(%rbx), %eax
    0x100003f22 <+50>: movsbl 0x3(%rbx), %eax
    0x100003f26 <+54>: movsbq 0x3(%rbx), %rax
Target 0: (a.out) stopped.
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f1e a.out`test + 46
a.out`test:
->  0x100003f1e <+46>: movsbl 0x2(%rbx), %eax
    0x100003f22 <+50>: movsbl 0x3(%rbx), %eax
    0x100003f26 <+54>: movsbq 0x3(%rbx), %rax
    0x100003f2b <+59>: leaq   0x8(%rbx,%rax), %rcx
Target 0: (a.out) stopped.
(lldb) p (void*) $eax
(void *) $2 = 0x0000000000000008
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f22 a.out`test + 50
a.out`test:
->  0x100003f22 <+50>: movsbl 0x3(%rbx), %eax
    0x100003f26 <+54>: movsbq 0x3(%rbx), %rax
    0x100003f2b <+59>: leaq   0x8(%rbx,%rax), %rcx
    0x100003f30 <+64>: leaq   0x8(%rbx,%rax,2), %rcx
Target 0: (a.out) stopped.
(lldb) p (void*) $eax
(void *) $3 = 0x0000000000000008
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f26 a.out`test + 54
a.out`test:
->  0x100003f26 <+54>: movsbq 0x3(%rbx), %rax
    0x100003f2b <+59>: leaq   0x8(%rbx,%rax), %rcx
    0x100003f30 <+64>: leaq   0x8(%rbx,%rax,2), %rcx
    0x100003f35 <+69>: retq
Target 0: (a.out) stopped.
(lldb) p (void*) $eax
(void *) $4 = 0x0000000000000055
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f2b a.out`test + 59
a.out`test:
->  0x100003f2b <+59>: leaq   0x8(%rbx,%rax), %rcx
    0x100003f30 <+64>: leaq   0x8(%rbx,%rax,2), %rcx
    0x100003f35 <+69>: retq
    0x100003f36 <+70>: nopw   %cs:(%rax,%rax)
Target 0: (a.out) stopped.
(lldb) p (void*) $rax
(void *) $5 = 0x0000000000000055
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f30 a.out`test + 64
a.out`test:
->  0x100003f30 <+64>: leaq   0x8(%rbx,%rax,2), %rcx
    0x100003f35 <+69>: retq
    0x100003f36 <+70>: nopw   %cs:(%rax,%rax)

a.out`main:
    0x100003f40 <+0>:  pushq  %rbp
Target 0: (a.out) stopped.
(lldb) p (void*) $rcx
(void *) $6 = 0x0000000100008072
(lldb) stepi
Process 53662 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = instruction step into
    frame #0: 0x0000000100003f35 a.out`test + 69
a.out`test:
->  0x100003f35 <+69>: retq
    0x100003f36 <+70>: nopw   %cs:(%rax,%rax)

a.out`main:
    0x100003f40 <+0>:  pushq  %rbp
    0x100003f41 <+1>:  movq   %rsp, %rbp
Target 0: (a.out) stopped.
(lldb) p (void*) $rcx
(void *) $7 = 0x00000001000080c7

作業ログをもとに課題ページで問われている質問とその解答の表を導出した

質問 解答
movl (%rbx),%eaxを実行した時の%eaxの値は? 0x0000000008070605
incl %rbxを実行してmovq (%rbx),%raxを実行した時の%raxの値は? 0x0000001255080706
movl 4(%rbx,%rax),%ecxを実行した時の%ecxレジスタの値がロードするメモリのアドレスは? 0x0000000108078624
movb 2(%rbx),%alを実行した時の%eaxの値は? 0x0000000000000008
movsbl 2(%rbx),%eaxを実行した時の%eaxの値は? 0x0000000000000008
movsbl 3(%rbx),%eaxを実行した時の%eaxの値は? 0x0000000000000055
movsbq 3(%rbx),%raxを実行した時の%raxの値は? 0x0000000000000055
leaq 8(%rbx,%eax),%rcxを実行した時の%rcxの値は? 0x0000000100008072
leaq 8(%rbx,%eax,2),%rcxを実行した時の%rcxの値は? 0x00000001000080c7

4.1

この問題を解く

以下の式を手で木に変換して見よ。さらに、これをスタックを使って計算するintel64の命令に落として見よ。examples のコンパイラを使った結果と比較してみよ。また、gdb で実際にどのような動作をするかを調べてみよ。
3-(4-2)
0+(1+(3-2))-(0+(1+(2-3)))

問題11.1

この問題を解く

記号処理とは文字などで表されたデータを処理することである。これに対して、音声解析や画像処理、ロボットの動作などは、非記号処理と言える。非記号処理におけるコンパイラの役割について、1000-2000字程度で考察せよ。

オートマトン

最終的な成績

Aでした

課題に関するファイル

これ

1.1 論文管理ソフト

この問題を解く

Agda に関する論文を探して Zoteroに登録する
登録したものの Bibtex を export して、それをレポートせよ
論文に対する短いコメントをつけること

Zoteroをダウンロードする

Agda に関する論文を探して Zoteroに登録する

登録したものの Bibtex を export して、それをレポートする

  • ファイルが追加されていることを確認したら、Add to Collection → New Collection... → automatonと入力して保存
  • ファイルを副クリックして、Export Item
  • Eport Itemの設定
  • assignment_1_1.bibとして保存
  • 中身を$ cat assignment_1.bib | pbcopyでもメモ帳で開いてでもいいので、クリップボードに張ってメールのレポートに貼る、bibファイルも添付する
@article{_gearsoshoare_2019,
    title = {{GearsOSのHoare} {Logicをベースにした検証手法}},
    volume = {IEICE-118},
    url = {https://ken.ieice.org/ken/paper/20190116d1Jm/},
    abstract = {Gears OS は継続を主とするプログラミング言語 CbC で記述されている。OS やアプリケーションの信頼性を上げるには仕様を満たしていることを確認する必要がある。現在 GearsOS の仕様の確認には定理証明系である Agda を用いている。CbC では関数呼び出しを用いず goto 文により遷移する。これは Agda 上では継続渡しの記述を用いた関数として記述する。また、継続にはある関数を実行するための事前条件や事後条件などをもたせることが可能である。Hoare Logic では事前条件が成り立っているときにある関数を実行して、それが停止する際に事後条件を満たすことを確認する。これは継続を用いた Agda 上では事前条件を継続で関数に渡し、関数からさらに継続した先で事後条件が成り立つという形で記述できる。本発表では GearsOS の仕様確認に Hoare Logic をベースとした記述と証明を導入する。},
    language = {ja},
    number = {IEICE-MSS-384,IEICE-SS-385},
    urldate = {2023-02-11},
    journal = {IEICE Conferences Archives},
    author = {政尊, 外間 and 真治, 河野},
    month = jan,
    year = {2019},
    note = {Publisher: The Institute of Electronics, Information and Communication Engineers},
    pages = {IEICE--MSS},
}

論文に対する短いコメントをつける

while文を使ったプログラムが正しく機能することをagdaで証明しているところが興味深かった。
これがきっかけで卒論ではデータベースをcbcで実装し、その機能の信頼性をagdaで証明したものを書きたいと思った。

2.2 Agdaによる関数と証明

この問題を解く

以下の agda の ? の部分を埋めよ。対応する証明図をコメントで書くこと。

解答

record1.agda

module record1 where

record _∧_ (A B : Set) : Set where
  field
     π1 : A
     π2 : B

ex0 : {A B : Set} → A ∧ B →  A
ex0 =  _∧_.π1 

ex1 : {A B : Set} → ( A ∧ B ) → A 
ex1 a∧b =  _∧_.π1 a∧b

open _∧_

ex0' : {A B : Set} → A ∧ B →  A
ex0' =  π1 

ex1' : {A B : Set} → ( A ∧ B ) → A 
ex1' a∧b =  π1 a∧b

ex2 : {A B : Set} → ( A ∧ B ) → B 
ex2 a∧b = π2 a∧b

--               [ A ]₁  [ B ]₂
--   ─────────────────────────────────────────── λ-elim
--                    A ∧ B
--   ─────────────────────────────────────────── λ-intro₂
--                    B → (A ∧ B)
--   ─────────────────────────────────────────── λ-intro₁
--                A → B → (A ∧ B)
ex3 : {A B : Set} → A → B → ( A ∧ B ) 
ex3 a b = record { π1 = a ; π2 = b }

--               [ A ]₁  [ A ]₂
--   ─────────────────────────────────────────── λ-elim
--                   (A ∧ A)
--   ─────────────────────────────────────────── λ-intro₁
--                    A → (A ∧ A)
ex4 : {A B : Set} → A → ( A ∧ A ) 
ex4 a  = record { π1 = a ;  π2 = a }

--                                   [B]₁  [C]₂
--                                 ───────────────────────────── λ-elim
--                     [ A ]₁          (B∧C) 
--   ─────────────────────────────────────────────────────────── λ-elim
--                          A    B       C
--   ─────────────────────────────────────────────────────────── λ-elim
--                         ( A ∧ B)      C
--   ─────────────────────────────────────────────────────────── λ-elim
--                              A ∧ (B∧C)
--   ─────────────────────────────────────────────────────────── λ-intro₁
--                    ( A ∧ B ) ∧ C  →  A ∧ (B ∧ C) 

ex5 : {A B C : Set} → ( A ∧ B ) ∧ C  →  A ∧ (B ∧ C) 
ex5 a∧b∧c = record { π1 = π1 (π1 a∧b∧c) ; π2 = record { π1 = π2 (π1 a∧b∧c) ; π2 = π2 a∧b∧c } }

--
--                                 [(A → B ) ∧ ( B →  C) ]₁  
--                                ──────────────────────────────────── π1
--     [(A → B ) ∧ ( B →  C) ]₁        (A → B )    [A]₂
--   ──────────────────────────── π2 ─────────────────────── λ-elim
--          ( B →  C)                     B
--   ─────────────────────────────────────────── λ-elim
--                   C
--   ─────────────────────────────────────────── λ-intro₂
--                 A → C
--   ─────────────────────────────────────────── λ-intro₁
--     ( (A → B ) ∧ ( B →  C) )  → A → C
ex6 : {A B C : Set} → ( (A → B ) ∧ ( B →  C) )  → A → C
ex6 x a = π2 x (π1 x a)

--         [ A ]₁  [ B ]₂
--   ──────────────────── λ-elim
--               B                  C
--   ─────────────────────────────────────────── λ-elim
--                       C
--   ─────────────────────────────────────────── λ-intro₃
--                     A → C
--   ─────────────────────────────────────────── λ-intro₂
--               ( B →  C) → A → C
--   ─────────────────────────────────────────── λ-intro₁
--          (A → B ) → ( B →  C)   → A → C
ex6' : {A B C : Set} →  (A → B ) → ( B →  C)   → A → C
ex6' = λ z z₁ z₂ → z₁ (z z₂)

実行

$ agda record1.agda && echo done
done

5.1 正規表現

この問題を解く

egrep / Perl などを使って、これらのパターンに対するテストプログラムを作成せよ。C で書くとどうなるか?

C の regcomp を使ってみよ。

perlで実装する

regex_match.perl

use strict;
use warnings;

my $target = "Brouhaha!";
my $regex = '(h|a)aha*';

while ($target =~ m/$regex/g) {
  my $match = $&;
  my $start = $-[0];
  my $end = $+[0];
  print "pattern match succeeded\n";
  print "Match: $match\n";
}

実行結果

$ perl regex_match.perl
pattern match succeeded
Match: haha

Cで実装する

regex_match.perl

#include <stdio.h>
#include <regex.h>
#include <stdlib.h>
#include <string.h>

int main(int argc, char *argv[]) {
  regex_t preg;
  int result, i, match_count;
  regmatch_t *pmatch;
  char *target = "Brouhaha!";
  int target_len = strlen(target);
  int offset = 0;

  // Compile the regular expression pattern
  result = regcomp(&preg, "(h|a)aha*", REG_EXTENDED);
  if (result != 0) {
    printf("fail to compile regex\n");
    return 1;
  }

  // Perform the pattern matching
  pmatch = malloc(sizeof(regmatch_t));
  result = regexec(&preg, target + offset, 1, pmatch, 0);
  match_count = 0;
  while (result == 0 && offset < target_len) {
    printf("pattern match succeeded\n");
    printf("Match: %.*s\n", (int)(pmatch[0].rm_eo - pmatch[0].rm_so), target + offset + pmatch[0].rm_so);
    match_count++;
    offset += pmatch[0].rm_eo;
    pmatch = realloc(pmatch, (match_count + 1) * sizeof(regmatch_t));
    result = regexec(&preg, target + offset, 1, &pmatch[match_count], REG_NOTBOL);
  }
  if (result != REG_NOMATCH) {
    printf("fail to execute regrex\n");
    return 1;
  }
  free(pmatch);

  regfree(&preg);

  return 0;
}

実行結果:

$ gcc regex_match.c -o regex_match && ./regex_match
pattern match succeeded
Match: haha

6.1 正規表現の決定性オートマトンへの変換

この問題を解く

以下の正規表現をDFAに変換せよ。

(1) (a*|b*)c

(2) (a|b)*c

(3) (a*|b*)c(a|b)*c

(4) ((a*|b*)c)|((a|b)*c)

draw.ioなどで以下の図を再現して、svgで出力して学科ページに貼ればいいじゃないかと思う、それか画像を直接メールに添付でもいいかもしれないがどうだろう...?

一応、学科ページにdraw.ioで再利用可能なxmlファイルとsvgファイルをアップロードした、ご自由に再利用してください

index.html

<!doctype html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <title>automaton 6.1</title>
  </head>
  <body>
    <h1>automaton 6.1</h1>
    <hr>
    <h2>(1) (a*|b*)c</h2>
    <img src="images/6.1.1.svg">
    <a href="download/6.1.1.xml" download>xmlファイルのダウンロード</a>
    <hr>
    <h2>(2) (a|b)*c</h2>
    <img src="images/6.1.2.svg">
    <a href="download/6.1.2.xml" download>xmlファイルのダウンロード</a>
    <hr>
    <h2>(3) (a*|b*)c(a|b)*c</h2>
    <img src="images/6.1.3.svg">
    <a href="download/6.1.3.xml" download>xmlファイルのダウンロード</a>
    <hr>
    <h2>(4) ((a*|b*)c)|((a|b)*c)</h2>
    <img src="images/6.1.4.svg">
    <a href="download/6.1.4.xml" download>xmlファイルのダウンロード</a>
  </body>
</html>

ファイル構成

+amane+e205723 ls
download/  images/  index.html
+amane+e205723 ls download
6.1.1.xml  6.1.2.xml  6.1.3.xml  6.1.4.xml
+amane+e205723 ls images
6.1.1.svg  6.1.2.svg  6.1.3.svg  6.1.4.svg
+amane+e205723 pwd
/home/student/e20/e205723/public_html/automaton/6-1
  • (1) (a*|b*)c
  • (2) (a|b)*c
  • (3) (a*|b*)c(a|b)*c
  • (4) ((a*|b*)c)|((a|b)*c)