疲れたよ…
来る日も来る日も,コンパイラが生成した,ほとんどアセンブリみたいな C 言語とにらめっこしつつ,バグを取る日々.そりゃぁ,精神も荒廃してきますよ.日記が壊れ気味でも気にしてはいけにゃい.
今日は,length とかをコンパイルしてみた.これは,末尾再帰ではなく,再帰を使わざるを得ない例題,という難しさがあるの〜.
// (length *リスト *リストの長さ)
(length () *N) --> (= *N 0).
(length (? | *Xs) *N) --> (length *Xs *N1), (:= *N (+ *N1 1)).
そうすると,問題は,今までのように goto だけでは上手くいかないのである.スタックかどっかに戻り番地を確保しておいて,処理が終わったら,終わったところから元の場所 (これが,いわゆる,アセンブリ言語のレベルでの,継続 continuation という値の正体.高級言語になればなるほど,もっといろいろな実行時の環境 (コンテキスト)情報が増える) まで飛んで処理を再開しないといけない.
しっかし,戻ってくるところのアドレスなんて,C みたいな高級言語からは,ポータブルには取れっこないよなぁ…
# ETI レベルでは,どこまでプリミティブにプログラム変換しようとも限界があり,ここまで低水準なところにはもともと触れないので,問題が表面化しない.
というわけで,今のところは,本当に関数を再帰呼び出しているのですが…
この方法だと,複数のお互いに呼び合う D ルールを,一つの巨大な C 関数に無理矢理融合変換,関数呼び出しなんて無し,内部で直接 goto でぐるぐる回りまくる,とかいうコンパイルができなくなるとか,いろいろと問題が.
GCC なんかだと,
みたいな,いろいろと無理があることを強引にすれば,なんとかなるかも.
5.3 Labels as Values
(このときの C スタックやら実行時コンテキストを setjmp かなんかで,どっかに丸ごと保存しておいて,&&LABEL といっしょにクローズしておけば,継続オブジェクトとかができる… のかもしれない.よく知らない.setjmp/longjmp とかもあんまり使ったことないし…)
もはや C 言語でも何でも無い !
関数を超えたジャンプも余裕.ダイクストラ先生もびっくりだ.
というか,無理やり,関数呼び出しという C 言語の構造化言語としての枠組みを使わないで,ネイティブコンパイラを書いたほうが楽なことっていっぱいあるんだよなぁ… でもポータビリティが無くなり過ぎるし,いまどきの CPU のレジスタ割り当てとかパイプライニングとかは,もう素人じゃどうしようもないレベルだし.やっぱり,C の歴史とポータビリティとベンダー提供の超絶コンパイラは捨てがたい.C-- とか良く知らないだけですけど…
それにしても,値としてラベルを使うことができる GCC は偉いよ.C って,やっぱり,いまいち中途半端に高級なんだよなぁ.リターンアドレスぐらい触らせてくれ〜 スタックフレーム弄らせてくれ〜 これぐらい標準化してくれ !! ていぅか関数呼び出しによる構造化なんていらねーよー 本当に,単なるポータブルなアセンブリ言語だったら良かったのに (無茶苦茶)
(いろいろ毒されてきて,もはや関数型どころか,構造化プログラミングすらどうでもよくなってきている管理人)
今日は,length とかをコンパイルしてみた.これは,末尾再帰ではなく,再帰を使わざるを得ない例題,という難しさがあるの〜.
// (length *リスト *リストの長さ)
(length () *N) --> (= *N 0).
(length (? | *Xs) *N) --> (length *Xs *N1), (:= *N (+ *N1 1)).
そうすると,問題は,今までのように goto だけでは上手くいかないのである.スタックかどっかに戻り番地を確保しておいて,処理が終わったら,終わったところから元の場所 (これが,いわゆる,アセンブリ言語のレベルでの,継続 continuation という値の正体.高級言語になればなるほど,もっといろいろな実行時の環境 (コンテキスト)情報が増える) まで飛んで処理を再開しないといけない.
スタック (ワード配列) に順番に引数積む;
ベース値を適切に書き換える;
(ベース + n が,n 番目のローカル変数のインデクスになる.テキトーに,スタック配列の 1 番目とかに確保してる.0 番目には現在のスタックの長さが入っている.スタックフレームの構造はいろいろ変わるかも.今はかなりテキトー)
goto length; // サブルーチンの最初に戻る.
次にここに戻ってきて,ここから処理を再開しないといけない.
しっかし,戻ってくるところのアドレスなんて,C みたいな高級言語からは,ポータブルには取れっこないよなぁ…
# ETI レベルでは,どこまでプリミティブにプログラム変換しようとも限界があり,ここまで低水準なところにはもともと触れないので,問題が表面化しない.
というわけで,今のところは,本当に関数を再帰呼び出しているのですが…
この方法だと,複数のお互いに呼び合う D ルールを,一つの巨大な C 関数に無理矢理融合変換,関数呼び出しなんて無し,内部で直接 goto でぐるぐる回りまくる,とかいうコンパイルができなくなるとか,いろいろと問題が.
GCC なんかだと,
bool
func(word stack[], word mem[]) {
...
dokka :
なんか処理;
goto (void *)stack[retValueIndex];
← 今ここをプログラムカウンタが指しているとする;
引数積む;
ワード配列に,無理やり void * を詰め込む;
stack[retValueIndex] = (word)&&continuationLABEL;
スタックポインタずらす.
goto dokka;
continuationLABEL :
継続処理;
...
}
みたいな,いろいろと無理があることを強引にすれば,なんとかなるかも.
5.3 Labels as Values
(このときの C スタックやら実行時コンテキストを setjmp かなんかで,どっかに丸ごと保存しておいて,&&LABEL といっしょにクローズしておけば,継続オブジェクトとかができる… のかもしれない.よく知らない.setjmp/longjmp とかもあんまり使ったことないし…)
もはや C 言語でも何でも無い !
関数を超えたジャンプも余裕.ダイクストラ先生もびっくりだ.
というか,無理やり,関数呼び出しという C 言語の構造化言語としての枠組みを使わないで,ネイティブコンパイラを書いたほうが楽なことっていっぱいあるんだよなぁ… でもポータビリティが無くなり過ぎるし,いまどきの CPU のレジスタ割り当てとかパイプライニングとかは,もう素人じゃどうしようもないレベルだし.やっぱり,C の歴史とポータビリティとベンダー提供の超絶コンパイラは捨てがたい.C-- とか良く知らないだけですけど…
それにしても,値としてラベルを使うことができる GCC は偉いよ.C って,やっぱり,いまいち中途半端に高級なんだよなぁ.リターンアドレスぐらい触らせてくれ〜 スタックフレーム弄らせてくれ〜 これぐらい標準化してくれ !! ていぅか関数呼び出しによる構造化なんていらねーよー 本当に,単なるポータブルなアセンブリ言語だったら良かったのに (無茶苦茶)
(いろいろ毒されてきて,もはや関数型どころか,構造化プログラミングすらどうでもよくなってきている管理人)
