YAPC::Tokyo2019に参加して, 踏み出せたこと

1月25日, 26日とYAPC::Tokyoに参加しました. YAPC自体はKansaiが初参加で, Okinawaではスタッフをしていた.

今回のTokyoでは, 初めてスピーカーとして出させていただき, 当日スタッフもやらせてもらった. 更に前夜祭と本編のLTも出させていただいた.本当にYAPCを楽しむことが出来た.参加者, 運営の皆様ありがとうございます.

前夜祭

スタッフをやらせていただいていたので早めに会場について, ノベルティの荷物詰めなどをしていた. 初めて実際にお会いする方が多かったが, 会場の雰囲気は非常に楽しくて, 疲れはしたが準備をする事が出来た.

前夜祭の資料は大体の枠組みは行きの飛行機と前日に書いていた. 今回は大学の卒業研究などを通して勉強していたMoarVMとNQP(Perl6の実行環境とサブセットの話)で登壇した.

NQP自体はPerl6の内部で利用されているもので, 皆さんの興味がNQPに向くか非常に不安だった. 登壇が終わったあとにid:papixさんから, 「本編に出れる」と言われた時は嬉しかったし, Twitterを見た感じ皆さんに楽しんでいただいたようで非常に良かった.

前夜祭では他にはてなインターン振りにid:cohalzさんとお会い出来たのが良かった. 思えばあの時から, YAPC::Tokyoが前を向かせて貰ったのだと思う.

本編

本編はRoom1の会場の司会などを担当していた.Twitterの中の人もしようかなと思っていたが, 資料を準備したかったのと, 写真を綺麗に取る自信があまり無かったので, Twitterはお願いしていた.

司会は本当にやりやすくて, 自由に喋る事が出来た.司会自体はそんなに上手くなかったかもしれないが, YAPCのスタッフとして関わる事が出来ていたのは嬉しかった. Okinawaの時はコアスタッフだったのだが, 精神を崩していた時期もあり, 本調子で振る舞う事が出来なかった気がする. 今回のYAPCは, しっかり責務を果たせたかなと思った.

初めての登壇は不思議と緊張しなかった.あのRoom1, そしてYAPCの会場が非常に温かい雰囲気であり, 心地よく喋る事が出来た. このスライドに至る前に, ビルドを追求していくにつれてmetaconfigを発見したりと非常に面白かった.

5分前のベルでペース配分を焦ってしまったのが心残りではあるが, 自分としては大体したかったことが出来たと思う. あのスライドは本当に皆さんに興味を持っていただけるか自信がなく, 当日人がくるかどうかも心配していた. いざ時間になると, 本当に皆さん移動し忘れているのではないかと思うほど来ていただいて, トークを聞いてくださっていた. トークの内容も, 皆さんツイートしていただいたり, 途中で盛り上がっていただいたりと, 本当に自分自身も信じられないほどだった.

本当にこれは自分自身だけが面白いと感じているのではないかとずっと思っていた. Roppongi.pmにも呼んでいただいたので, 今考えると周囲の方々も面白いと思っていただいていたと思うのだけれど, あの内容は個人的には誰でも出来ると思ってしまっていて, そんなことを発表していいのかとちょっと思ったりもしていた.

そして登壇して, 席に戻って, タグを見たり, その後の懇親会や二次会, スライドを公開した上でのSNSなどで, 本当に意外なまでに皆さんに好評的な評価を頂いた. これは本当に自分も信じられなくて, その後miyagawaさんにまでNiceなどと言われて, 本当にこれが自分自身に起こっている事なのか信じられなかった. 僕だけが楽しいと思っていた事が, 皆さんにも楽しいと伝わったこと.本当に信じられない気持ちだった.

LTではgolangにPerl1.0を書き換えている話をした.これ自体は結構前から思いついていて, 実際に手を動かし始めたのはYAPCのLTに応募してからだった.

書いてみるとPerl1.0の内部構造がトレースのたびに理解出来るようになった気がして, 非常に楽しく夢中になれた. 当日までに動かすものを作りたかったが, なかなか厳しくて, ふわっとした話になってしまったのは反省している. ただ年内には出せるようにしたいので, 今後も開発していこうと思った. これも, ふわっとした話なので, そんなに評価されないかななどと思っていたが, Twitterを見ると様々な人に言及されていて嬉しかった.

懇親会ではいろいろな人とお会いする事が出来た.YAPCの参加回数が増えている為知り合いが増えてきたのもある気がする. 個人的には皆さんPerl1.0の話とか, Acmeモジュールの話やPerl6と言った好きな話題をする事が出来て, ありがたかった. id:utgwkkさんと初めてリアルで会えたのもいい体験だった.

不思議な感覚は他にもあった.id:ssabcireくんにTwitterで見ていて影響を受けたとか, 入学式の鹿さんに助けてもらったとか, id:kiryuanzuさんにエントリを見たとか言われた. 個人的にはそんなに頑張ってないし, やっているつもりも無かったのだが, そう言っていただけると嬉しかったし, 同時に何か不思議な気持ちになった.

その後は以前のエントリにも書いたが, id:papixさんに自信を持てと言われて, 運営の打ち上げの後に, 秋葉原で飲んでいた皆さんと合流した. id:mackee_wさんやid:xtetsujiさんが待っていただいたのが嬉しかった.

飲み会id:kiryuanzuさんと話しているうちに, 自分が本当に好きだったこと, 大切にしたい事が思い出せたように感じる. そこで, 思い出せた事は, YAPC::Kansaiの時のあの純粋な好奇心と, 自分が好きなことをする気持ちだった. YAPC::Okinawaやハッカーズチャンプルー, いつもの.pmなどで, 少しずつ取り戻しつつあった, 大切な感覚が取り戻せたと思っている. なんというか, 本当に肩の荷が下がり, 気持ちよくプログラミングや情報技術に迎える様になった.

今回のテーマは「報恩謝徳」だった.本当に今回のYAPC::Tokyoでは皆さんから恩をいただく事が出来た. 本当に, 楽しかった以外の感想がないほど楽しかったし, やっていくぞと, 本当に思うことが出来た. Perlコミュニティの皆さんにはいつも助けられてばかりだし, id:papixさんに至っては, 様々な意味でいつも励まされている. 沖縄でPerlを始めるきっかけとなったid:codehexさんとまたお会い出来たし, 本当に様々な人に支えられているんだなと感じた. この恩をいつか返せるように, そして新しくPerlYAPCに出会った方に, 僕がしていただけたことを逆に出来るように, 今後もやっていきたいと思った.

そういえばいつもYAPCに行くと元気になる気がする.やはりYAPCは人生を変えると思う.YAPC::Kansaiに行ってなければPerlを書いていなかったが, YAPC::Tokyoに行ってなければ, 今後Perlを書いてなかった気がする.

なんとなく同じ内容になってしまったが, まぁいいとして...

YAPC::Tokyo2019で感じたこと

(感想は別で書く予定で, これはYAPCを通じて考えたエモい話です)

最近(1年くらい)は好きなことに対して自信が持てなかった気がする. YAPC::Kansaiの時などは, 勉強したてのPerlAcmeモジュールが好きで, 書いているAcmeモジュールのロジックがコピペだろうと正直作っているだけで楽しかった. 最近の精神状態では, 意味を理解しないとロジックを書くべきではないとか思ってしまって多分書いてなかった気がする. Perlを書くこと.Acmeモジュールを使ったり読むこと.Perlの本を読んで勉強すること. その全てが純粋に楽しくて,その習得状況や書くコードの質などの差は確かにあるかもしれないが, それには本来優劣などは存在しない行為だと思う. 誰かと戦う為にジョークモジュールを作ることはないし, 何か強制的にコードを書く必要も本来はなかった. 純粋に楽しいからしていて,ただそれだけが活動のベースにあったのだと思う.

しかし最近は, 何をやっても.ことさら好きなはずのコンピュータのことでは, 人と比べることや, その意味を考えるようになってしまった. 「これを勉強してもxxxさんはすでに高校時代に実装していたんだよなぁ...」とか「こんな駄目なコードしか僕は書けないのに,xxxさんはこんなにもOSSを書いたりしている...」とかとかとか. 能力の差や技術力の差は確かに存在している.バズるサービスを開発する能力や, 綺麗なウェブページを作る能力, 難しい言語処理系のシステムを臆すること無くCで実装する事が出来る能力. それらを持っている人と, それらが今すぐに欲しくなってしまって, 自分の現状と比べて辛くなる事が多くなってしまった.

確かにPerlはずっと書いていたが, 最近書くPerlは楽しんで書くというよりは, 何かの為に書く事が多い気がしていた. 最初Perlを勉強した時にがむしゃらに書いていた, あのときの楽しさは無かった気がする.

YAPC::Tokyoのしばらく前, Roppongi.pmの前の時に, 何故か昔のPerlをビルドすることをしていた. 最初は唐突にPerlの初期バージョンに興味がわき, ぐぐったらコードがビルドできたのでおもちゃの様に遊んだ. 詳しくは覚えていないのだが, LTのネタに使えるかなと思ってやったかもしれない.

あの時.昔のPerlをビルドしている時は, 今考えるとKansaiのあの時に, Acmeモジュールを書こうと思って苦戦していた際の精神状況に似ていた気がした. そのコードややる事の意味などはどうでもよく,ただ個人的にやりたいからやっていた.面白そうだから.ただそれだけの純粋な理由だった.

思ったことはそう.僕は例えば何かのサービスを作らなければならないとか.早くしなければならないとか.そういう使命感みたいなのが動作すると途端に気力が無くなってしまうという性質なのかもしれないということだ. 最近webアプリを書く事が以前に比べて好きになれなかったのだが, 考えると, webの勉強をしなければならないという使命感が常につきまとい, その結果重くのしかかった気持ちに純粋な好奇心が負けてしまい, 死んでいたのだと思う.

好きなことは自由な時に自由にやりたい.それが仕事で出来るかはわからないが, 多分僕はそういう感じで技術とか変わっていくのが良い気がしていた.当然やらなければならないドリブンでせざるを得ないものもあるし, そういう時は多少辛くなると思っている.

しかし今思っているのは, そのやろうとしている事が本心で, 純粋な気持ちで面白いと思っているかどうかが重要だということだ. そしてその気持ちに優劣などはなくて, 好きなことは誰との比較も承認も必要なく, 好きと言い張って良いということだ. 好きなもので好きなことを好きようにやる.人生そううまくは行かないし, 業務の内容が好きかどうかなどは必ずすべてがそうではないと思う. だけれども, 自分自身の趣味の時間とか, やっていきたい気持ち自体は, 自分自身で認めていきたいと思った. やはり自分が本当に面白いと思っていることは, 人に喋っていて面白さが伝わるし, みんなに気持ちが伝わると思う. おそらく, 今まで僕が見てきたwebとかのすごい学生や人々は, その純粋な気持ちがたまたまwebに向いていただけで, 彼らは純粋な気持ちを持っている為に優劣などを考える事はなくて, 成長していけているのだと思う.

今は自分自身が何が面白いのか, 取り戻すのと掴むのに時間を要すると思っている. しかしその時間を焦る事をせず, 人々と比べずにやっていきたいと思った.

この考え方に追いついたのは, 個人的に自分だけが楽しいと思っていた昔のPerlをビルドする話をYAPCに採択していただいたこと.

当日のトークで, 自分が面白いと思った事に, 来ていただいた皆さんが同じ様に面白いと思って頂いた事.

id:papixさんに自信を持てと言われたこと, その後の飲み会で id:kiryuanzuさんと過去の話から様々なことを話せた事.

Twitterやブログで予想外に様々な人々から反響をいただいた事などがつながったからだと思う.全員のidを乗せるとすごい事になってしまうので書けないのですが, 北海道から沖縄, 海外や画面の向こうの人まで, 本当に皆さんありがとうございます.

YAPC::Tokyoでは3本も前に出させてもらった.どれも楽しかった.これはこの3本がすべて自分が面白いと思っていた事だからだと思う. 純粋な気持ちで面白いと思える事が, 僕は特にPerlに多いようだ.自分自身気づかなかったが, 好きなことを話している事はかなり楽しそうらしい. 今までのやらなければならないベースの考え方を脱却するのは人間なので難しいのだが, だけれども, 少しずつ純粋な気持ちを取り戻したいと思った.

完全にとりとめのないが, とりあえず今まとめとかないと駄目だと思ったのでまとめておいた.YAPCの感想はあとで書きます

継続を基本とするC言語CbCのご紹介

この記事は琉大情報工学科(知能情報コース) Advent Calendar 2018 の16日目の記事です.

どうせなのでCasl2のエミュレーターでも書くかという気になりましたが,今日はドラゴンボールを見てしまったのでエミュレーターは途中までとなりました.ご了承下さい.

さて今日は一部界隈でまことしやかに盛り上がっている謎の言語CbCことContinuation Based Cについての話です. ただしCbCは沼なので間違えている可能性があります.サイレント修正される可能性がありますがご了承下さい.

Continuation Based Cとは

Continuation Based C(以下CbC)とは継続を基本としたC言語の下位言語です. C言語での関数呼び出しやfor文などのループ文をコードから消滅させ, 状態遷移単位でコードを書くことが出来る言語です. 応用例としては世界最速のgrepやGearsOS, Perl6処理系のCbCMoarVMなどが存在します.

現在はgcc及びllvm/clang上に実装した2種類が存在します.

C言語を使ってプログラミングをする場合,メモリのアロケートやエラーハンドリングなどを記述していく必要があります. しかしこの処理は複雑かつエラーを発生させやすい為,通常の処理と分離して記述することが望ましいとされます. これらの処理をMetaComputationと呼びます. しかしC言語を使ったプログラミングである程度規模が大きいものを行おうとすると,これらの処理と通常の計算処理を分離して記述することは非常に難しいとされます.

CbCでは,関数の代わりにCodeSegmentとDataSegmentを基本単位として導入する事で,これらをやりやすくします.

CodeSegment

CbCでは関数の代わりにCodeSegmentを利用します.

CodeSegmentは関数よりも小さく,ステートメントよりも大きい単位となっています. CodeSegmentを利用したCbCではループ文を持ちません. これはCodeSegmentがコンパイラにおける基本ブロックと呼ばれる単位に該当する為です.

CodeSegmentはCの関数の代わりに __code と書くことで宣言出来ます. これは __code という型が有るわけではなく, CbCプログラマからはCodeSegmentである事を示す指示子の様な役割を果たします.

__code 自体はCbCコンパイラではvoid型として扱います.

CodeSegmentからCodeSegmentへはCのgoto文を利用して遷移します. この遷移はCの関数呼び出しとは異なり,callなどの命令を利用せずjmpを利用した軽量継続で行います. 実際にコード例を見てみましょう.

extern int printf(const char*,...);
  int main (){
     int data = 0;
     goto cg1(&data);
  }
  __code cg1(int *datap){
(*datap)++;
    goto cg2(datap);
}
__code cg2(int *datap){
    (*datap)++;
    printf("%d\n",*datap);
}

このコードはmain関数で設定した data = 0 をcg1と言うCodeSegmentに入力として渡します. この時点ではCの世界からCbCの世界に突入する段階ですので, cg1自体はcallで呼び出されます.

cg1では受け取ったdataのアドレスからdataをインクリメントし data = 1 にします. インクリメントの後 cg2 に軽量継続します. cg2では同様にdataをインクリメントし, printするという流れです.

#include <stdio.h>

__code fact(int n, int result,__code (*print)()) {
    if (n > 0) {
        result *= n;
        n--;
        goto fact(n,result,print);
    } else {
        goto (*print)(result);
    }
}

__code print_result(int result){
    printf("result is %d\n",result);
}

int main(int argc, char** argv){
    goto fact(20,0,print_result);
}

他には上記のような階上を求めるコードも書くことが可能です. fact(int n, int result,__code (*print)()) の引数として __code のCodeSegment自体を渡しています.

この様にCodeSegmentの引数にはCodeSegment自体もいれることが可能です. この引数の事をDataSegmentもしくはInterfaceと呼びます. なぜ引数ではなくDataSegmentと呼称するかについては後述します.

軽量継続とは

先程から何回か出ている軽量継続とは何でしょうか. 軽量継続ではなく,継続とはSchemaなどの処理系には実装されています.

Wikipediaによると

継続は、前の状態を引き継ぐこと。持続、保持。 計算機科学における継続(けいぞく、continuation)とは、プログラムの実行においてある時点において評価されていない残りのプログラム(the rest of the program)を意味するものであり、手続き(procedure)として表現される

https://ja.wikipedia.org/wiki/%E7%B6%99%E7%B6%9A

詳しくは http://practical-scheme.net/docs/cont-j.html

つまり,CbCではCレベルで次の処理の命令をよりSchemaなどの関数型言語のように記述することが出来るインターフェイスを提供します. この際にSchemaなどでは,現在の位置などを環境として保存する必要がありますが, CbCの場合そのあたりを保存しない為軽量な継続,つまり軽量継続と読んでいます.

うれしいところ

Cで軽量継続を使えると何が嬉しいのでしょうか. それはCの関数呼び出しがコストがかかる事がまず挙げられます.

Cの関数呼び出しでは, 呼び出し側は引数を積み上げ,戻り番地のセーブを行い, 呼び出される方は局所変数を保存したり,スタックやフレームポインタを押し上げるなどの処理が必要となります. これらを一切行わず,プログラムカウンタを変更するのみのjmp命令にCbCのgotoを利用したCodeSegmentは変換する事が可能です.

その為CodeSegmentを直接指定してgotoするなどの処理が書けるようになり,煩わしいfor文やcase-switch文をなくすことが可能です

Cでも末尾再帰と呼ばれる方法を利用すればこの方法が可能であり,実際CbCは内部的に末尾再帰アルゴリズムを用いて最適化しています.

その為, CodeSegmentのDataSegment部分を同じにする事で,全ての命令がjmp命令にコンパイルされます. jmp命令になるということは,CodeSegment間の引き渡しで引数がレジスタに乗ったまま移動されるということです.最高ですね.

つまりCodeSegmentの引数は入力のみではなく入出力としての意味を持つ為,引数ではなくData SegmentやInterfaceという呼び方をしています.

FizzBuzz

試しにFizzBuzzを書いてみます

#define __environment _CbC_environment
#define __return _CbC_return

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

__code fizzbuzz(int,int,char*,__code(),void*);
__code say2(int,int,char*,__code(),void*);
__code fizz(int,int,char*,__code(),void*);
__code buzz(int,int,char*,__code(),void*);

int main(void){
    int n = 100;
    fizzbuzz(1,n,"",__return,__environment);
    return 0;
}

__code fizzbuzz(int i,int n,char* ret_result,__code(*return1)(),void *return1env) {
    if ( i <= n ) {
        goto fizz(i,n,ret_result,return1,return1env);
    } else {
        goto return1(0,return1env);
    }
}


__code fizz(int i,int n,char* ret_result,__code(*return1)(),void *return1env) {
    if (i % 3 == 0){
        ret_result = "fizz";
    } else {
        ret_result = "";
    }
    goto buzz(i,n,ret_result,return1,return1env);
}

__code buzz(int i,int n,char* ret_result,__code(*result1)(),void *return1env) {
    char *result;
    result = (char *)malloc(15);
    if (i % 5 == 0){
        snprintf(result,9,"%s%s",ret_result,"buzz");
    } else {
        snprintf(result,8,"%s",ret_result);
    }
    goto say2(i,n,result,result1,return1env);
}

__code say2(int i,int n,char* ret_result,__code(*return1)(),void *return1env) {
    if ( ret_result[0] == '\0'){
        printf("%i : %i\n" ,i,i);
    } else {
        printf("%i:%s\n",i,ret_result);
    }
    goto fizzbuzz(i+1,n,"",return1,return1env);
}

CbCっぽく書くコツはDataSegmentをそろえる事です.

これを-Sをつけてアセンブラを見てみましょう.

cbclang -S fizzbuzz.cbc

 .section  __TEXT,__text,regular,pure_instructions
    .macosx_version_min 10, 14
    .globl    _main..ret0             ## -- Begin function main..ret0
    .p2align  4, 0x90
_main..ret0:                            ## @main..ret0
    .cfi_startproc
## %bb.0:                               ## %entry
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq (%rsi), %rax
    movl %edi, (%rax)
    movq 8(%rsi), %rax
    movq (%rax), %rbp
    movq 8(%rax), %rsi
    movq 16(%rax), %rsp
    jmpq *%rsi
    .cfi_endproc
                                        ## -- End function
    .globl    _main                   ## -- Begin function main
    .p2align  4, 0x90
_main:                                  ## @main
    .cfi_startproc
## %bb.0:                               ## %entry
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq %rsp, %rbp
    .cfi_def_cfa_register %rbp
    pushq    %r15
    pushq    %r14
    pushq    %r13
    pushq    %r12
    pushq    %rbx
    subq $264, %rsp              ## imm = 0x108
    .cfi_offset %rbx, -56
    .cfi_offset %r12, -48
    .cfi_offset %r13, -40
    .cfi_offset %r14, -32
    .cfi_offset %r15, -24
    leaq -208(%rbp), %rax
    leaq -252(%rbp), %rcx
    leaq _main..ret0(%rip), %rdx
    movq ___stack_chk_guard@GOTPCREL(%rip), %rsi
    movq (%rsi), %rsi
    movq %rsi, -48(%rbp)
    movl $0, -212(%rbp)
    movl $100, -216(%rbp)
    movl -216(%rbp), %esi
    movq %rdx, -224(%rbp)
    movq -224(%rbp), %rdx
    movq %rdx, -232(%rbp)
    movq -232(%rbp), %rdx
    movq %rcx, -248(%rbp)
    movq %rax, -240(%rbp)
    movq -240(%rbp), %rax
    movq %rax, %rcx
    movq %rbp, %rdi
    movq %rdi, (%rax)
    movq %rsp, %rdi
    movq %rdi, 16(%rax)
    leaq LBB1_8(%rip), %rax
    movq %rax, 8(%rcx)
    movl %esi, -268(%rbp)        ## 4-byte Spill
    movq %rdx, -280(%rbp)        ## 8-byte Spill
    #EH_SjLj_Setup LBB1_8
## %bb.6:                               ## %entry
    xorl %eax, %eax
    movl %eax, -284(%rbp)        ## 4-byte Spill
LBB1_7:                                 ## %entry
    movl -284(%rbp), %eax        ## 4-byte Reload
    movq -240(%rbp), %rcx
    movq %rcx, %rdx
    movq %rbp, %rsi
    movq %rsi, (%rcx)
    movq %rsp, %rsi
    movq %rsi, 16(%rcx)
    leaq LBB1_11(%rip), %rcx
    movq %rcx, 8(%rdx)
    movl %eax, -288(%rbp)        ## 4-byte Spill
    #EH_SjLj_Setup LBB1_11
## %bb.9:                               ## %entry
    xorl %eax, %eax
    movl %eax, -292(%rbp)        ## 4-byte Spill
LBB1_10:                                ## %entry
    movl -292(%rbp), %eax        ## 4-byte Reload
    cmpl $0, %eax
    je   LBB1_2
## %bb.1:                               ## %if.then
    movl -252(%rbp), %eax
    movl %eax, -212(%rbp)
    jmp  LBB1_3
LBB1_2:                                 ## %if.end
    leaq -248(%rbp), %rax
    movq %rax, -264(%rbp)
    movq -264(%rbp), %rax
    leaq L_.str(%rip), %rdx
    movl $1, %edi
    movl -268(%rbp), %esi        ## 4-byte Reload
    movq -280(%rbp), %rcx        ## 8-byte Reload
    movq %rax, %r8
    callq    _fizzbuzz
    subq $8, %rsp
    movl $0, -212(%rbp)
LBB1_3:                                 ## %return
    movl -212(%rbp), %eax
    movq ___stack_chk_guard@GOTPCREL(%rip), %rcx
    movq (%rcx), %rcx
    movq -48(%rbp), %rdx
    cmpq %rdx, %rcx
    movl %eax, -296(%rbp)        ## 4-byte Spill
    jne  LBB1_5
## %bb.4:                               ## %SP_return
    movl -296(%rbp), %eax        ## 4-byte Reload
    addq $264, %rsp              ## imm = 0x108
    popq %rbx
    popq %r12
    popq %r13
    popq %r14
    popq %r15
    popq %rbp
    retq
LBB1_5:                                 ## %CallStackCheckFailBlk
    callq    ___stack_chk_fail
LBB1_8:                                 ## Block address taken
                                        ## %entry
    movl $1, %eax
    movl %eax, -284(%rbp)        ## 4-byte Spill
    jmp  LBB1_7
LBB1_11:                                ## Block address taken
                                        ## %entry
    movl $1, %eax
    movl %eax, -292(%rbp)        ## 4-byte Spill
    jmp  LBB1_10
    .cfi_endproc
                                        ## -- End function
    .globl    _fizzbuzz               ## -- Begin function fizzbuzz
    .p2align  4, 0x90
_fizzbuzz:                              ## @fizzbuzz
    .cfi_startproc
## %bb.0:                               ## %entry
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq $48, %rsp
    cmpl %esi, %edi
    movl %esi, -4(%rbp)          ## 4-byte Spill
    movq %r8, -16(%rbp)          ## 8-byte Spill
    movq %rcx, -24(%rbp)         ## 8-byte Spill
    movq %rdx, -32(%rbp)         ## 8-byte Spill
    movl %edi, -36(%rbp)         ## 4-byte Spill
    jg   LBB2_2
## %bb.1:                               ## %if.then
    movl -36(%rbp), %edi         ## 4-byte Reload
    movl -4(%rbp), %esi          ## 4-byte Reload
    movq -32(%rbp), %rdx         ## 8-byte Reload
    movq -24(%rbp), %rcx         ## 8-byte Reload
    movq -16(%rbp), %r8          ## 8-byte Reload
    addq $48, %rsp
    popq %rbp
    jmp  _fizz                   ## TAILCALL
LBB2_2:                                 ## %if.else
    xorl %eax, %eax
    movb %al, %cl
    movl %eax, %edi
    movq -16(%rbp), %rsi         ## 8-byte Reload
    movb %cl, %al
    movq -24(%rbp), %rdx         ## 8-byte Reload
    callq    *%rdx
    addq $48, %rsp
    popq %rbp
    retq $8
    .cfi_endproc
                                        ## -- End function
    .globl    _fizz                   ## -- Begin function fizz
    .p2align  4, 0x90
_fizz:                                  ## @fizz
    .cfi_startproc
## %bb.0:                               ## %entry
    subq $56, %rsp
    .cfi_def_cfa_offset 64
    leaq L_.str.1(%rip), %rax
    movl $3, %edx
    movq %rax, 48(%rsp)          ## 8-byte Spill
    movl %edi, %eax
    movl %edx, 44(%rsp)          ## 4-byte Spill
    cltd
    movl 44(%rsp), %r9d          ## 4-byte Reload
    idivl    %r9d
    cmpl $0, %edx
    movq 48(%rsp), %r10          ## 8-byte Reload
    movl %esi, 40(%rsp)          ## 4-byte Spill
    movq %r8, 32(%rsp)           ## 8-byte Spill
    movq %rcx, 24(%rsp)          ## 8-byte Spill
    movl %edi, 20(%rsp)          ## 4-byte Spill
    movq %r10, 8(%rsp)           ## 8-byte Spill
    je   LBB3_2
## %bb.1:                               ## %if.else
    leaq L_.str(%rip), %rax
    movq %rax, 8(%rsp)           ## 8-byte Spill
    jmp  LBB3_2
LBB3_2:                                 ## %if.end
    movq 8(%rsp), %rax           ## 8-byte Reload
    movl 20(%rsp), %edi          ## 4-byte Reload
    movl 40(%rsp), %esi          ## 4-byte Reload
    movq %rax, %rdx
    movq 24(%rsp), %rcx          ## 8-byte Reload
    movq 32(%rsp), %r8           ## 8-byte Reload
    addq $56, %rsp
    jmp  _buzz                   ## TAILCALL
    .cfi_endproc
                                        ## -- End function
    .globl    _buzz                   ## -- Begin function buzz
    .p2align  4, 0x90
_buzz:                                  ## @buzz
    .cfi_startproc
## %bb.0:                               ## %entry
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq $64, %rsp
    movl $15, %eax
    movl %eax, %r9d
    movl %edi, -4(%rbp)          ## 4-byte Spill
    movq %r9, %rdi
    movl %esi, -8(%rbp)          ## 4-byte Spill
    movq %r8, -16(%rbp)          ## 8-byte Spill
    movq %rcx, -24(%rbp)         ## 8-byte Spill
    movq %rdx, -32(%rbp)         ## 8-byte Spill
    callq    _malloc
    movl $5, %esi
    movl -4(%rbp), %r10d         ## 4-byte Reload
    movq %rax, -40(%rbp)         ## 8-byte Spill
    movl %r10d, %eax
    cltd
    idivl    %esi
    cmpl $0, %edx
    jne  LBB4_2
## %bb.1:                               ## %if.then
    movl $9, %eax
    movl %eax, %esi
    xorl %edx, %edx
    movl $15, %eax
    movl %eax, %ecx
    leaq L_.str.2(%rip), %r8
    leaq L_.str.3(%rip), %rdi
    movq -40(%rbp), %r9          ## 8-byte Reload
    movq %rdi, -48(%rbp)         ## 8-byte Spill
    movq %r9, %rdi
    movq -32(%rbp), %r9          ## 8-byte Reload
    movq -48(%rbp), %r10         ## 8-byte Reload
    movq %r10, (%rsp)
    movb $0, %al
    callq    ___snprintf_chk
    movl %eax, -52(%rbp)         ## 4-byte Spill
    jmp  LBB4_3
LBB4_2:                                 ## %if.else
    movl $8, %eax
    movl %eax, %esi
    xorl %edx, %edx
    movl $15, %eax
    movl %eax, %ecx
    leaq L_.str.4(%rip), %r8
    movq -40(%rbp), %rdi         ## 8-byte Reload
    movq -32(%rbp), %r9          ## 8-byte Reload
    movb $0, %al
    callq    ___snprintf_chk
    movl %eax, -56(%rbp)         ## 4-byte Spill
LBB4_3:                                 ## %if.end
    movl -4(%rbp), %edi          ## 4-byte Reload
    movl -8(%rbp), %esi          ## 4-byte Reload
    movq -40(%rbp), %rdx         ## 8-byte Reload
    movq -24(%rbp), %rcx         ## 8-byte Reload
    movq -16(%rbp), %r8          ## 8-byte Reload
    addq $64, %rsp
    popq %rbp
    jmp  _say2                   ## TAILCALL
    .cfi_endproc
                                        ## -- End function
    .globl    _say2                   ## -- Begin function say2
    .p2align  4, 0x90
_say2:                                  ## @say2
    .cfi_startproc
## %bb.0:                               ## %entry
    pushq    %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset %rbp, -16
    movq %rsp, %rbp
    .cfi_def_cfa_register %rbp
    subq $64, %rsp
    movsbl   (%rdx), %eax
    cmpl $0, %eax
    movl %esi, -4(%rbp)          ## 4-byte Spill
    movq %r8, -16(%rbp)          ## 8-byte Spill
    movq %rcx, -24(%rbp)         ## 8-byte Spill
    movq %rdx, -32(%rbp)         ## 8-byte Spill
    movl %edi, -36(%rbp)         ## 4-byte Spill
    jne  LBB5_2
## %bb.1:                               ## %if.then
    leaq L_.str.5(%rip), %rdi
    movl -36(%rbp), %esi         ## 4-byte Reload
    movl -36(%rbp), %edx         ## 4-byte Reload
    movb $0, %al
    callq    _printf
    movl %eax, -40(%rbp)         ## 4-byte Spill
    jmp  LBB5_3
LBB5_2:                                 ## %if.else
    leaq L_.str.6(%rip), %rdi
    movl -36(%rbp), %esi         ## 4-byte Reload
    movq -32(%rbp), %rdx         ## 8-byte Reload
    movb $0, %al
    callq    _printf
    movl %eax, -44(%rbp)         ## 4-byte Spill
LBB5_3:                                 ## %if.end
    movl -36(%rbp), %eax         ## 4-byte Reload
    incl %eax
    leaq L_.str(%rip), %rdx
    movl %eax, %edi
    movl -4(%rbp), %esi          ## 4-byte Reload
    movq -24(%rbp), %rcx         ## 8-byte Reload
    movq -16(%rbp), %r8          ## 8-byte Reload
    addq $64, %rsp
    popq %rbp
    jmp  _fizzbuzz               ## TAILCALL
    .cfi_endproc
                                        ## -- End function
    .section  __TEXT,__cstring,cstring_literals
L_.str:                                 ## @.str
    .space    1

L_.str.1:                               ## @.str.1
    .asciz    "fizz"

L_.str.2:                               ## @.str.2
    .asciz    "%s%s"

L_.str.3:                               ## @.str.3
    .asciz    "buzz"

L_.str.4:                               ## @.str.4
    .asciz    "%s"

L_.str.5:                               ## @.str.5
    .asciz    "%i : %i\n"

L_.str.6:                               ## @.str.6
    .asciz    "%i:%s\n"


.subsections_via_symbols

実際にCodeSegment同士はjmpで,mallocなどの関数呼び出しのみcallになっている事がわかります

~/w/c/S/cbc-sandbox » grep jmp fizzbuzz.s
    jmpq    *%rsi
    jmp LBB1_3
    jmp LBB1_7
    jmp LBB1_10
    jmp _fizz                   ## TAILCALL
    jmp LBB3_2
    jmp _buzz                   ## TAILCALL
    jmp LBB4_3
    jmp _say2                   ## TAILCALL
    jmp LBB5_3
    jmp _fizzbuzz               ## TAILCALL
~/w/c/S/cbc-sandbox » grep call fizzbuzz.s
    callq   _fizzbuzz
    callq   ___stack_chk_fail
    callq   *%rdx
    callq   _malloc
    callq   ___snprintf_chk
    callq   ___snprintf_chk
    callq   _printf
    callq   _printf

試すには

試すには gccはこちら

clangはこちらを見ていただくと出来ます.

なおclangが容量をすごく取りますが,現状Mac os Mojaveではclangしかビルド出来ません...助けてくれ...

Perl入学式第2回目をPerl6で解いてみる(Part1)

皆さんこんにちは.最近はようやく寒くなってきた沖縄からid:anatofuzです.

そういえばYAPC::Tokyoのチケットはまだ売っている様です.皆さん行きましょう.

yapcjapan.org

今日は雰囲気でPerl6でPerl入学式第2回の構文基礎を書いていこうと思います

Perl6とは

Perl6とはPerlっぽい別言語です. Python2とPython3の関係ではないので注意しましょう.

とは言え,最初にエイヤッとした言語デザイナーがLarryWallでもありますし, 幾つかのSyntaxはPerlに似ています.

Hello,World!

print "Hello, World!\n"; このように書いたものを, hello.plとして保存します printは, 端末に文字を出力します \nは改行を表します 最後に;を忘れずに!

Perl6ではprintもありますが,よく使うのはsayです.

say "Hello,World!";

ちなみに他にこういうやり方もあります

"Hello,World!\n".print;

これはMuクラスに生えているprintメソッドを利用した場合です. https://docs.perl6.org/routine/print

perl5までのprintはIOクラスのメソッドとなっています

print "Hello,World!\n";

もちろんsayもメソッドとして生えているので出来ます

"Hello,World!".say;

おまじない

!/usr/bin/env perl

use strict; use warnings; おまじないとして, 冒頭の3行を書くようにしよう use strict -> 厳密な書式を定めたり, 未定義の変数を警告するといった効果があります use warnings -> 望ましくない記述を警告してくれる効果があります 以下, この資料のサンプルコードではこれを「お約束」として省略します 書かれているものとして扱ってください

Perl6ではstrictやwarningsの様なものは標準で入っています. この代わりのおまじないと言えば,シェバングと次の1行が該当するでしょうか.

#!/usr/bin/env perl6
use v6;

このv6とはPerl5で仮にこのPerl6プログラムを実行した際にエラーを出してくれるというものです. Perl6的には特に害が無いです. https://docs.perl6.org/language/101-basics#v6

例えば次の様なスクリプトを書いたとしましょう.

use v6;
print "hoge\n";

2行目に関してはPerl5でもprintがあるので,実行することが可能ですが, 仮に他の処理があると予期せぬ処理をPerl5がしそうです. 現在のPerl6では普通のスクリプトの拡張子をp6, クラス定義などのモジュールになるファイルの拡張子をpm6とすることを推奨していますが,Perl5と同じplが拡張子として使われている場合もあります. このプログラムは use v6; をしているので, Perl6で実行すると次のようなエラーを出します.

~/.sandbox » perl hoge.p6
Perl v6.0.0 required--this is only v5.26.2, stopped at hoge.p6 line 1.
BEGIN failed--compilation aborted at hoge.p6 line 1.

という訳で1行読み込んで終わりました.安全性のために書いておくのがオススメです.

復習問題

Hello, Perl Entrance!という文字列を出力するhello_perl.plを書いて下さい

say "Hello, Perl Entrance!";

構文チェック

perl6では perl -c hoge.p6 とするといい感じになります.

~/.sandbox » perl6 -c hoge.p6
Syntax OK

スカラ変数

Perl6はPerl5と似ており,スカラ,配列,ハッシュの3タイプに変数が分類されます.

Perl6のドキュメントによると次の様に書かれています https://docs.perl6.org/type/Scalar

A Scalar is an internal indirection which is for most purposes invisible during ordinary use of Perl 6. It is the default container type associated with the $ sigil. A literal Scalar may be placed around a literal value by enclosing the value in $(…). This notation will appear in the output of a .perl method in certain places where it is important to note the presence of Scalars.

When a value is assigned to a $-sigiled variable, the variable will actually bind to a Scalar, which in turn will bind to the value. When a Scalar is assigned to a $-sigiled variable, the value bound to by that Scalar will be bound to the Scalar which that variable was bound to (a new one will be created if necessary.)

In addition, Scalars delegate all method calls to the value which they contain. As such, Scalars are for the most part invisible. There is, however, one important place where Scalars have a visible impact: a Scalar will shield its content from flattening by most Perl 6 core list operations.

A $-sigiled variable may be bound directly to a value with no intermediate Scalar using the binding operator :=. You can tell if this has been done by examining the output of the introspective pseudo-method .VAR:

雰囲気で理解すると,Perl6におけるスカラ型は $ で宣言した変数名と,実際の値をバインドしてくれる中間的なクラスであり,$ で宣言すると自動的に全てスカラになるということの様です.

また := を利用することで,間にスカラクラスを挟まず,ダイレクトに値を束縛することが可能です.

とは言え,基本的にはPerl5と同じ扱いですが,実際に型として利用されるのはバインドされている変数ということの様です.

これは公式ドキュメントによると次の様に見ることが可能です.

my $a = 1;
$a.^name.say;     # OUTPUT: «Int␤»
$a.VAR.^name.say; # OUTPUT: «Scalar␤»
my $b := 1;
$b.^name.say;     # OUTPUT: «Int␤»
$b.VAR.^name.say; # OUTPUT: «Int␤»

.^name で変数の型が確認できます. $a では1をスカラクラス経由でバインドしており,基底にScalarがあることがわかります.

一方 $b は1を束縛している為,アイテム化されておらず,$b そのものがIntであることがわかります.

Perl6では $ があるかないかでScalarクラスであるかどうかが変わります.

.say for (1, 2, 3);           # OUTPUT: «1␤2␤3␤», not itemized
.say for $(1, 2, 3);          # OUTPUT: «(1 2 3)␤», itemized
say (1, 2, 3).VAR ~~ Scalar;  # OUTPUT: «False␤»
say $(1, 2, 3).VAR ~~ Scalar; # OUTPUT: «True␤»

上の例を見ると $ があるかないかで変わっていることがわかりますね. この $(1,2,3) はアイテム化されているといい, $(1,2,3) でひとまとめです.

その為 $(1,2,3) は実はこうなります.

> my @hoge = $(1,2,3);
[(1 2 3)]
> say @hoge
[(1 2 3)]
> @hoge[0]
(1 2 3)
> @hoge[0][0]
1

アイテム化されている為,実は $(1,2,3) はスカラであり,その中の要素はリストなので配列のようにアクセスするというPerl5っぽい挙動を示します.

コメント

Perl6のコメントはPerl5と同じく# です

# こうするとコメント

またPerl6ではMulti-line commentも追加されました

"#`" とカッコでくくると,その間がコメントとなります.

if #`( why would I ever write an inline comment here? ) True {
    say "something stupid";
}

この例では #`の後に ( が来ているので,この中が全部コメントとなり 最終的にはif True { という感じに解釈されます.

$perl6 hoge.p6
something stupid

これは結構便利ですね!!

疲れたので続きは次回...

YAPC::Tokyo 2019に「レガシーPerlビルド 〜現代に蘇るPerl[1..5].0とPerl6〜」 で登壇します

皆様こんにちは.最近はMoarVMと格闘している id:anatofuzです.

YAPC::Tokyoに応募していたトークが通り,登壇することになりました.

yapcjapan.org

タイトルは「レガシーPerlビルド 〜現代に蘇るPerl[1..5].0とPerl6〜」です.

このトークでは以前Roppongi.pmでお話させていただいた内容をベースに次のような内容で喋ります.

みなさんが今使用しているPerlのバージョンはいくつですか?5.24?5.28?はたまたPerl6...? 互換性を大事にするPerlと言っても,みなさんが今動かしているPerlはPerl5以降のものかと思います.一部Perl4のプロジェクトがあるかもしれませんが.... でも待ってみて下さい,なにか忘れていませんか...?

そうです.Perl"5"と言うことは,Perl1からPerl4がかつて存在していたという事です.もし,Perl2.0やPerl3.0のソースコードが目の前にあったら動かしてみたくなりませんか?

このトークでは過去のPerlソースコードから,動きのしくみ,そして現代のosではどのようにすれば過去のPerlが動くのかについて解説します. バージョンごとの特性や, C言語で書かれたPerl処理系のおもしろポイントと,年代別の修正ポイントも一気に見ていきます. また, 実際にPerl1からPerl5,Perl6まで一気に走らせた場合どの様な処理速度の違いが出るのかなどについても紹介します.

対象とするのはPerl1から現在の主流であるPerl5,そしてレガシーと言いつつ最先端の(?)Rakudo Perl6です. メインはPerl5までのPerlですが,Perl6の内部構造などについてもお話するかもしれません.

という訳で具体的には以下のような事をお話しすると思います(変わるかもしれませんが...)

アジェンダ(仮)

20分で勢いよく見ていくのでご期待下さい!

ちなみにPerl[1..6]まで20分で見るので1バージョン3分くらいの勢いなので頑張っていこうな

基本的には初心者向け(Perlのビルドに初心者も何もいるのかというのは置いといて)ですが, 以下のようなパッションをお持ちの方は特に興奮すると思います.

  • Perl自体に興味が有る方
  • トリビア的に無駄な知識を入れることに興奮する
  • 今の職場で1980年代のコードを動かす必要がある
  • 上司にPerl2.0を動かすように言われた
  • 転生した先ではPerl1.0しか無かった

とう言うわけで1月26日(土)に浅草ヒューリックホール&ヒューリックカンファレンスで僕と握手!!!!!

たのしいPerl6 その1

こんにちは id:anatofuz です.これは琉大情報工学科(知能情報コース) Advent Calendar 2018 4日目の記事です.

今日は皆さん大好きなPerl6についてです.

Perl6とは

Perl6に関しては僕の卒論を見ていただけると良いかと思います

Perl6とはPerlっぽい別言語です. 誤解されることが多いですがPython2とPython3の関係ではなく,どちらかというとJavaJavascriptっぽい関係です.

当初はPerl5の次期バージョンとして開発されていましたが, 開発が進むに連れて互換性の無さや新規文法がもりもり増えてしまった事で「あれ,これPerlじゃなくね?」という事で別の言語となりました.

Perl6は設計と実装が分離しており,主要な実装はRakudoです. 現在の設計はテストスイートRoastを,実装に関してはドキュメントを見るという世界です.

ちなみにRakudoの由来は駱駝道 ( 🐫道) です.なぜ🐫かというとPerlのマスコットキャラがラクダであるからだと思います.

現在はもはやPerl6から別名にしようという動きが一部であり,「6lang」にしようという流れを得て,現在は「Raku」という別名がついています.

Perl6の入れ方

実はPerl6はPythonやPerl5の様に 「perl6っていうコマンドが一つある」 訳ではありません.

実はPerl6を実行するコマンドperl6はシェルスクリプトで.実態はmoarというバイナリにライブラリパスなどを設定して実行しているものです.

これはPerl6がNQPと呼ばれるサブセットで書かれており, このNQPをMoarVMやJVMが解釈するという構成になっている為です. つまり実際に動かすのはperl6ではなくMoarVMのバイナリmoarです. ではPerl6をinstallする際には,この辺を個別でインストール必要があるのでしょうか.

実はそんなことも無く,rakudo-starというPerl6に必要なものをひとまとめにしたツールが配布されています.

(ちなみに rakudo-starのstarはシェルなどのワイルドカード* を使ったrakudo-* の意味です)

Macの場合

macの場合は二種類あり,公式からdmgをdlしてパスを通す 方法か, brewで入れる方法があります.

brewの場合は

$ brew install rakudo-star

これで入ります.

Perl6で遊ぼう

では試しにPerl6のREPLで遊んでみましょう. Perl5とは違い, Perl6はデフォルトでperl6と打つとREPLモードになります.

$perl6
To exit type 'exit' or '^D'

落ち着いて素数を数えよう

実はPerl6には遅延評価があり,しかも数学の無限大を示すInfクラスがあります. これを組み合わせると如何のようにな配列を作れます.

> my @primes = (^∞).grep: *.is-prime;
[...]

これは ^ がrangeクラスのコンストラクタで,0から^ の右の値未満の範囲を作るという意味です.つまり0から無限大のクラスです.

これにメソッドチェーンでgrepをつなげます.grepは配列からある条件を満たす要素を出すものです.

この *.is-prime は,その中の要素が is-prime つまり素数であるならば真を返します.

そして返り値を受け取る my @primes はPerl5と同じく配列の宣言です.

> の次はPerl6のREPLが返した値ですが [...] とありますが,これは配列だが中身はまだ計算してないということです.

本当に素数が入っているか見てみましょう. Perl6で配列の要素を参照するには @array[0] などとします. これを出力するには,メソッドチェーンでsayを繋げます.

> @primes[0].say
2
> @primes[1].say
3
> @primes[2].say
5
> @primes[3].say
7
> @primes[4].say
11
> @primes[5].say
13

素数が入ってそうですね!!!

FizzBuzz

FizzBuzzはいろいろな書き方がありますがtitsukiさんの以下のページが面白いです.

qiita.com

特に面白いのが,Perl6は漸進的型付言語と呼ばれる特徴があり,型が無いようにも有るようにも振る舞う事が可能です.

その特徴を活かすと次のようなfizzbuzzがかけます

my subset Fizz of Int where * %% 3;
my subset Buzz of Int where * %% 5;
my subset FizzBuzz of Int where Fizz&Buzz;
my subset OtherNumber of Int where none Fizz|Buzz;

proto sub fizzbuzz ($) { * }
multi sub fizzbuzz (FizzBuzz) { "FuzzBuzz" }
multi sub fizzbuzz (Fizz) { "Fizz" }
multi sub fizzbuzz (Buzz) { "Buzz" }
multi sub fizzbuzz (OtherNumber $number) { $number }

fizzbuzz($_).say for 1..15;

これはsubsetでFizzの型,Buzzの型,FizzBuzzの型,それ以外の型を定義します. multi で関数に引数の型と応じた関数の内容を記述する事が出来,今回はそれを使って型でfizzbuzzをしています.面白いですね.

という訳でPerl6の話でした.結構面白いと思うのでぜひ遊んでみてはいかがでしょうか.