読者です 読者をやめる 読者になる 読者になる

Rust で LLVM やってみる

Rust と LLVM やってみたくなってやってる. llvm-sys という LLVM の Rust バインディングのライブラリを使って LLVM 動かしてみた話と自分用メモ.

きつねさんでもわかるLLVMを読んでからやるかと思ったが,5章以降はちょっと自分には合わなかったのでブログ探して、それをやりながら参照程度に狐さんを見るという作戦でいった.

環境設定

何とかして Rust を入れる. これを真似すればだいたい行ける.

気付いたらRustの環境構築がかなり楽になってた | κeenのHappy Hacκing Blog

後は LLVM を入れてpathを通す.

brew install llvm
export PATH="`brew --prefix llvm`/bin:$PATH"

やったこと

  • LLVM IR の雰囲気を知りたかったので幾つかサンプル書いてみた.

playground/rust/llvm-example at master · ganmacs/playground · GitHub

llvm*.rsは上の方に動かしたい擬似コードを書いてある. codegen*.rs は AST ができてる体でそれを LLVM IR に変換するコードをかいた.

  • Rust で作った言語(中途半端なlisp) のバックエンドを LLVMにしてみた.

ほとんど Rust かいた経験がないため,色々おかしいところがありそう. マクロかけるようにするかってところで気持ちが終わり,LLVMバックエンドにリプレイスとかやってる.

GitHub - ganmacs/rlisp

参考になったブログとコード

llvm の公式のサイト.実際 LLVM IR 書いてる(実際に書いてるのはRustのラッパーだけど)と使い方がわからず,ググると大抵これしかヒットしないので一生懸命これを読む.

Rust で llvm-sys.rs を使って 数字を出すところから phi 関数(というのがある) を使って if 式を作ることまでやってる.

Go 言語用のバインディングもあるようでそれを使って,加算するところまでやってる.序盤の説明が分かりやすかった.

LLVM の外観をつかむのには良かった. ただ,読み進めてるといきなり Pass の話をしだすので,LLVM IR について知りたい場合は序盤だけ読んで,後半は後で読むと良さそう.

LLVMチュートリアルを Rust でやってる. この作者が作った iron-llvm という llvm-sys.rs をラップしたライブラリを使って LLVM を触っているのでいきなりこれを見ること混乱するかもしれない. 最初はむしろ iron-llvm のコードを読んで llvm-sys.rs の使い方の雰囲気をつかむのが良い.

きつねさんでもわかる LLVM のサンプルコードのリポジトリ.割りと大きめのコードなので実際に真似しやすい. 構文の定義( https://github.com/Kmotiko/DummyCCompiler/blob/master/dummyC_ebnf.txt )を読めばどんな言語かわかるので本の方は読まなくてもだいたい分かると思う.

emacsでコード読んでるときに「現在いる行が含まれたプルリク」に飛びたいを見れるようにした

hisaichi5518.hatenablog.jp

これ見て hub browse -- commit/<commit-id> でcommitに飛べることを知ったので、それを使って emacsでコード読んでるときに「現在いる行が含まれたプルリク」に飛びたいを見れるようにした。 このスクリプトも元ブログと一緒でプルリクのページではなくコミットページを開くところで終わってますが。

(defun open-github-commit ()
  (interactive)
  (let* ((cmd1 "git blame -l -L %s,+1  %s | cut -d ' ' -f 1")
         (cmd2 "hub browse -- commit/%s")
         (zero "0000000000000000000000000000000000000000")
         (commit-id (shell-command-to-string (format cmd1 (line-number-at-pos) buffer-file-name))))
    (if (string= zero commit-id)
        (message "This line is not commited")
      (shell-command (format cmd2 commit-id)))))

(global-set-key (kbd "M-g o") 'open-github-commit)

さよならgheに対応してないelispGitHub - ganmacs/emacs-github-open: Open a commit url in github.com

Treasure Data インターンで最高の夏過ごしてきた #td_intern

8月1日から9月30日の間,Treasure Data Summer Intern 2016に参加していました. 他のインターン生2人からの強烈なプレッシャー (強烈なプレッシャー1強烈なプレッシャー2)に負けたので僕もインターンブログを書きます.

tl;dr

すごいメンターの元 Fluentd の開発してると何故かめっちゃお金がもらえるインターンでとにかく最高

なにやってたの?

f:id:ganmacs:20161007102854p:plain キュートなロゴ

Fluentd というソフトウェアの v0.14 向けの機能を,メンターの @tagomoris さんの元,開発しました.具体的に行ったことは以下のようになります(スライドからそのまま持ってきただけですが).

  • 6 features
    • Counter API (Not merged yet)
    • Data compression in buffer plugins and forward plugins
    • New out_file plugin only for section
    • A CLI tool to read dumped event data
    • Log rotation
    • filter_with_time method in filter plugins
  • 2 enhancements
    • Optimizing multiple filter calls
    • Add event size to options in a forward protocol
  • Some small tasks

インターン期間は2ヶ月あったので,最初の1ヶ月は Fluentd の開発スタイルに慣れようということで小さめの機能(Counter API以外のタスク)をいくつかをやって,最後の1ヶ月で大きい機能(Counter API)を1つ をやりました.

一応雑に機能を紹介すると,

Counter API
マルチプロセスでfluentd動かしたとき,メトリクスとるのに使えるやつ

Data compression in buffer plugins and forward plugins
データを圧縮して操作できるようになり何かとリソースを節約できるやつ

New out_file plugin only for section
secondary セクションのみで使えるシンプルな out_file プラグイン

A CLI tool to read dumped event data
送信に失敗してダンプされた MessagePack形式のデータを読めるようなるやつ

Log rotation
Fluentd で log rotation できるようになるやつ

filter_with_time method in filter plugins
filter pluginで time をいじれるようにするやつ

Optimizing multiple filter calls
複数filter使用したときに高速化するやつ

Add event size to options in a forward protocol
eventのサイズつかって,内部の無駄な処理最適化するやつ

それぞれの説明を詳しく書いてもいいんですが長くなりすぎて誰も読まなくなると思うので,興味のある人は僕に会ったときにでも聞いてください(スライドを読んでもいいですが説明がアレ).

以下が最終発表スライドです.発表は英語でするはずだったんですが,僕のときだけUSの人たちがいなくて日本語で発表OKだったので,日本語が使えることに感謝しながら日本語で発表しました.

感想は?

まずは,Fluentd がいろいろ大変でした(語彙力).そもそも1回くらいしか Fluentd 使ったことなかったのでどうやって使われているか調べるところからはじまり,「あれ,これやばくない???」とか思いながらインターン初日を過ごしていました. @tagmorisさんのスライドにも書いてありますが,Rubyの黒魔術をふんだんに使って書かれていたので非常に読み応えがありました. コードを読む上で,Fluentd ソースコード完全解説 は参考になりました.結構古いので変わってしまったところもありますが,全体の流れを見るにはちょうどよかったです. キッっと睨んでるとわかってきたので大変なのは慣れることだなって感想になりました.

あとは,一番大きい機能の Counter API を merge まで持っていけなかったことが心残りです. 設計をしてレビューのフェーズで大量に時間を使ってしまったのがよくなかったなって感じです. 今の自分ができることと,できないことがはっきりしたのでそういう意味では良かったかなとおもっています.

前々から興味のあったミドルウェア開発を経験できたことは(しかもFluentdで,しかもTDで)とても幸運で,最高の経験だったと思っています. 開催されるかしらないですが,もし来年も開催されならとりあえず出してみると意外とインターン行けるかもしれないので,来年も学生の皆様はぜひ.

まとめ

特にまとめることもないですが,とても刺激的なインターンだったということが伝われば幸いです. 周りは全員超すごい人ばかりだったので,「(業務中自分の力の無さに)おちこんだりもしたけれど、私はげんきです。」という感じです. 社員のみなさん,同期の2人にはとてもお世話になりました.ありがとうございました.

付録1(愉快な仲間たち)

僕の他に2人,インターン同期がいました. 彼らが先に書いたブログがあって僕のより数倍いいのでそちらもぜひ.

@amay382 iPhone7とiOSのことをめちゃくちゃ煽る人(年下なのに優秀でつらい)

トレジャーデータでインターンしてた話 #td_intern - 水底

@takuti オタクかヤンキーかで言えばオタクの人(英語が引くほどうまい)

Treasure Dataインターンにみる機械学習のリアル #td_intern | takuti.me

2人とも機械学習(系?っていうの?あってる?)をやってたので,「インターンでは何やるの?」って聞いたんですけど,丁寧に教えてもらった割に何言ってるよくわからず「すごそう」くらいしか感想がなかったのできっとすごいんだろうなって思ってました.

付録2(食)

牡蠣が苦手だった僕も牡蠣が食べられるようになりました。

以下、 ログの重要性について真面目にディスカッションしていたときの図です!!!! f:id:ganmacs:20160930214523j:plain f:id:ganmacs:20160930194731j:plain f:id:ganmacs:20160930200710j:plain f:id:ganmacs:20160930214021j:plain

高速にメモをとるためのOrg Capture

Org Capture とはOrg-Modeのメモ取りツール(わかりやすく書いてある -> 色々 Org Capture する | Amrta )

以下のように書いておくと(キーバインドはなんだっていい)

(setq org-capture-templates
   '(("t" "Task" entry (file (expand-file-name (concat org-directory "/task.org")))
      "* TODO %?\n    %i\n   %a\n    %T")
     ("n" "note" entry (file (expand-file-name (concat org-directory "/notes.org")))
      "* %?\n   %a\n    %T")
     ("r" "reading" entry (file (expand-file-name (concat org-directory "/reading.org")))
      "* %?\n   %a\n    %T")))

(global-set-key (kbd "C-c C-q") 'org-capture)

C-c C-qと押した時に以下のようなバッファが現れどれかを選択するとメモが簡単に書ける

Select a capture template
=========================

[t]     Task
[n]     note
[r]     reading
-------------------------------------------------------------------------------
[C]     Customize org-capture-templates
[q]     Abort

ただ問題があって,たかがメモをとるためにC-c C-q nとおさないとメモを取れないのはだるすぎる(C-c C-qの部分は好きに設定できるが最小でも2回必要.しかも画面が勝手に開いて視点がコロコロ変わり本当にだるい) そこで以下のような関数を作った.これでC-M-=を一度押せばいきなりメモをかけるようになった. org-captureの第二引数を上記で記述したorg-capture-templatest(Task)かn(note)かr(reading)にすることで指定のテンプレートを使用できる.

(defun org/note-right-now (content)
  (interactive "sContent: ")
  (org-capture nil "n")
  (insert content)
  (org-capture-finalize))
(global-set-key (kbd "C-M-=") 'org/note-right-now)

Dired でバイナリファイルを開くときに本当に開くか確認する何か

環境

動機

Emacsでは写真やらPDFやらをEmacs内で見れるイッてる機能があります.
しかし,写真やらPDF開こうとすると数秒かかって非常に辛いです. さらに,Diredを使っているとよく押し間違えてPDFを開いてしまい,数秒待ったあとkill-bufferするみたいなことによくなります. これが非常に辛いのでなんとかしたいということで書きました.

実際

(defun util/same-ext? (file-path ext)
  "Check same exntion or not."
  (let ((file-ext (file-name-extension file-path)))
    (if file-ext
        (string= (downcase file-ext) ext))))

(defun util/chomp (str)
  (replace-regexp-in-string "[\n\r]" "" str))

(setq dired-open-whitelist '("c" "coffee" "clj" "el"
                             "ex" "exs" "go" "h" "hs"
                             "html" "js" "ml" "md" "rb"
                             "yml" "scala" "slim" "scss"))

(defun dired/binary? ()
  (let ((cmd (format "nkf -g %s" (dired/filename-at-point))))
    (string= "BINARY" (util/chomp (shell-command-to-string cmd)))))

(defun dired/filename-at-point ()
  (car (dired-get-marked-files 'no-dir)))

(defun dired/directory? ()
   (file-directory-p (car (dired-get-marked-files))))

(defun dired/include-whitelist? (filename whitelist)
  (if whitelist
      (or (util/same-ext? filename (car whitelist))
          (dired/include-whitelist? filename (cdr whitelist)))))

(defun dired-file-open-or-not ()
  "File open if file type is directory or file exntion is in whitelist or file is not binary."
  (interactive)
  (let ((ext-lst dired-open-whitelist)
        (filename (dired/filename-at-point)))
    (if (or (dired/directory?)
            (dired/include-whitelist? filename ext-lst)
            (not (dired/binary?)))
        (dired-find-file)
      (if (yes-or-no-p (format "Do you really open? %s" filename))
          (dired-find-file)))))

後は

(define-key dired-mode-map (kbd "C-f") 'dired-file-open-or-not)

何をしたか

開くのに時間がかかりそうなファイルを開いていいかどうかを聞くようにしました.

ファイルがBINARYかどうかをnkfで確認してます. しかし,毎回nkfを叩くのは辛いので以下の条件の時はnkfを叩かずそのままファイルを開いています.

  • ディレクトリの時
  • whitelistで定義された拡張子のファイルの時

感想

これを書いてたら何故か時間が消えていったが後悔はしていない

emacsからalcでの単語検索を楽にする

emacsからアルクで単語検索する便利コマンド書いた

環境
コード
(defsubst marked-input ()
  (when (use-region-p)
    (buffer-substring-no-properties (region-beginning) (region-end))))

(defun search-word-in-alc ()
  (interactive)
  (let* ((cmd "open \"%s\"")
         (url (format "http://eow.alc.co.jp/search?q=%s"
                      (or (marked-input) (read-shell-command "word: ")))))
    (shell-command-to-string
     (format cmd url))))

(global-set-key (kbd "s-E") 'search-word-in-alc)

ちなみにzshバージョンもある

function search-word-in-alc() {
    url="http://eow.alc.co.jp/search?q="
    open "${url}$1"
}
alias e='search-word-in-alc'

エスケープ処理とか全くしてないので動きが怪しいこともある気がするけどそこそこ便利!!!

RubyでDSLを書く

Rackのコードを読んでたらDSL使ってたのでその部分. instance_evalを使ってBuilderクラス内でblockを実行するようにしている.

つまり以下のプログラムは,Builderのインスタンス内で呼ばれたことになってる.

  map '/' do
    {
      'Content-Type' => 'text/plain',
      'status' => 200,
      'body' => 'this is root!'
    }
  end
class Builder
  def initialize(&block)
    instance_eval(&block) if block_given?
  end

  def map(path, &block)
    set(:get, path, block)
  end

  def get(path)
    key = [:get, path]
    route_table[key].call
  end

  private

  def set(name, path, block)
    key = [name, path]
    route_table[key] = block
  end

  def route_table
    @route_table ||= {}
  end
end

app = Builder.new do
  map '/' do
    {
      'Content-Type' => 'text/plain',
      'status' => 200,
      'body' => 'this is root!'
    }
  end
end

p app.get('/')
# => {"Content-Type"=>"text/plain", "status"=>200, "body"=>"this is root!"}

まとめ

最高の夏

参考

http://ref.xaio.jp/ruby/classes/object/instance_eval