移植(2)

先週の枕には、散歩中に出会った猫達について書いたけど、今週は犬達についてです。

幾ら田舎だって、犬が勝手に一人(一犬が正しいな)で散歩してる訳ではありません。野良猫 もどきは居るけど、野良犬はいないマナーが良い土地です。

この時期、太陽が昇ってくると熱くなっちゃうので、散歩の時間は大体朝の5時ぐらいからかな。 犬の散歩は大体おじいちゃんおばあちゃんの仕事と決まっているみたいで、年寄りが伴っています。

りんご畑の細い道、わんちゃんの糞撤去にいそしむおばあちゃんがいました。おばあちゃんがそちらの 方に気を取られ、わんちゃんが道を塞いでいる事に気づかずです。で、おいらは、

ちょーと通してくださんしぇー と、合図を送りました。大きな黒い犬が人なつこく くんくんと鼻を寄せてきましたよ。飼い主がそばに居ると、犬は悪さをしないものなのね。

やっと気づいたおばーちゃん、犬のリードを引いて道を作ってくれました。そのまま立ち去るのも 不作法と思い、何歳ですか? って、とっさに声をかけちゃった。言ってしまった後で、おいら ちょっと焦ったね。わんちゃんって頭に付ければ良かったよ。

でも、おばーちゃんはちゃんとコンテキストを理解してくれていて、もう9歳になるんですよ、と 返してきた。おじいちゃんで、白い物も混じり始めてますって、言葉の穂をつないでくれた。

お子さんはどのくらい? って聞いたら、たくさん居すぎて分からないとか。おじーちゃんと 言う事は、男犬。普通去勢しちゃうんじゃないかと思って聞いてみると、おじーちゃん(飼い主さん) が、可愛そうだと言う事で、去勢はしなかったとの事。それで元気一杯に動き回れるんでしょう との事。 幸せな犬だ事。

幸せな犬と言うと、散歩の途中で疲れてしまって、飼い主さんにだっこされてる犬も見かけるな 。これって老々介護?

最近、『約束の森』(角川書店)なんていう本を読んだ。犬が出て来るとっても素敵な物語。月に17-8冊の 本を濫読してるけど、久々に、犬も歩けば棒に当たるって感じでしたね。あれ? このたとえ 使い方を間違ってないかい?

犬も歩けば棒に当たる
【読み】 いぬもあるけばぼうにあたる
【意味】 犬も歩けば棒に当たるとは、でしゃばると思わぬ災難にあうという戒め。
         また、じっとしていないで、何でもいいからやってみれば思わぬ幸運に
         あうことのたとえ。 
         The waiking dog finds a bone.(犬も歩けば骨に会う)

一番先に読むもの

先週からSimpleって言う教育用SchemeをFreeBSDに移植しようとしてるんだけど、あえなくcoreを 吐いてお亡くなりになってしまうんだ。

そこでソースを読んでみよとなる訳だが、どこが一番美味しいソースなんだろう? ライオンが 獲物を捕らえた時、真っ先に食べるのは内臓だそうだけど、彼らは一番価値のある部分を知ってる んだろうね。(内臓は一番栄養豊富な部分らしいよ)

ソースの場合は、各*.cに共通に表れる部分をまとめた、*.hが、一番真っ先にみなければならない 部分なんだろうね。 (参考にどうぞ) そんな訳で、simp.hをつらつらと見て行くと、Schemeのご本尊様が定義 されてたよ。

ご本尊様のcellに、car,cdr領域のみならず、数値やら名前(シンボル)やら何でも押し込んじゃうって、非常に 分かり易い設計になってる。そして、それらを簡単にアクセス出来るようにマクロが定義されてる。 どんなマクロがあるかと言うと、

#define GET_CAR(addr)           memory[addr].car
#define SET_NAME(addr,x)        memory[addr].name = (char *)malloc(SYMSIZE); strcpy(memory[addr].name,x);
#define SET_CHAR(addr,x)        memory[addr].name[0] = x; memory[addr].name[1] = NUL;
#define IS_SYMBOL(addr)         memory[addr].tag == SYM

等々、GET系、SET系、確認系の3種類だ。その他で注目は、SYMとかの定義かな。冒頭付近に 定義されてるので、頭に入れておくと吉。

後、忘れてならないのは、SECDの他にレジスターが拡張されている事。

/*------register----*/
int S;                          /*stack */
int E;                          /*environment */
int C;                          /*code */
int D;                          /*dump */
int G;                          /*global environment pointer */
int H;                          /*heap pointer */
int F;                          /*free cell */

用途が決まっているので、ソース上に出てきても、迷う事は無いだろう。

大きすぎるcore

FreeBSDで実行してセグフォで落ちるんだけど、coreが非常に大きい。200Mを超えるんだ。 LinuxだとCoreは出来ないけど、実行中は、やはり200Mもメモリーを喰う。ちょっと負担なので、 cellのサイズを小さくしておいたよ。

ついでに、一つのcellがメモリー上でどれぐらいを占めているか、確認しておくと、

#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include "simp.h"

int main(void)
{

    printf("sizeof(struct cell)=%ld\n", (long) sizeof(struct cell));

    printf("offsets: tag=%ld\n", (long) offsetof(struct cell, tag) );
    printf("offsets: flag=%ld\n", (long) offsetof(struct cell, flag) );
    printf("offsets: name=%ld\n", (long) offsetof(struct cell, name) );
    printf("offsets: real=%ld\n", (long) offsetof(struct cell, real) );
    printf("offsets: imag=%ld\n", (long) offsetof(struct cell, imag) );
    printf("offsets: env=%ld\n", (long) offsetof(struct cell, env) );
    printf("offsets: car=%ld\n", (long) offsetof(struct cell, car) );
    printf("offsets: cdr=%ld\n", (long) offsetof(struct cell, cdr) );

    exit(EXIT_SUCCESS);
}

実行してみる。

[sakae@secd ~/Simple]$ ./a.out
sizeof(struct cell)=40
offsets: tag=0
offsets: flag=4
offsets: name=8
offsets: real=12
offsets: imag=20
offsets: env=28
offsets: car=32
offsets: cdr=36

各メンバーの位置も出しておいた。変な詰め物は無かったね。

clangでコンパイル

Linuxのgccで普通に使えるのが出来、FreeBSDのgccではダメだった。ひょっとしてFreeBSDのgccが おかしいんじゃないっと、動かないのは他人のせいにする病が発症。

じゃ、FreeBSD9.0Rなら、clangがあるから、そっちでやってみるか。(今、ArchLinuxで確認したら、 clangも使えるようになってた)

[sakae@bang ~]$ pacman -Ss clang
extra/clang 3.1-2
    C language family frontend for LLVM
extra/clang-analyzer 3.1-2
    A source code analysis framework

余談はさておき、makefileのgccとなってる所を 全てclangに偏向。走らせてみると、大量の警告が出てきた。

clang -g -c main.c
main.c:16:20: warning: implicit conversion from enumeration type 'enum toktype' to different enumeration type 'backtrack' (aka 'enum backtrack') [-Wconversion]
token stok = { GO, OTHER };
             ~     ^~~~~
main.c:35:5: warning: implicit declaration of function 'f_load' is invalid in C99 [-Wimplicit-function-declaration]
    f_load(list1(makesym("simpmacs.scm")));
    ^
      :
cell.c:401:16: warning: using the result of an assignment as a condition without parentheses [-Wparentheses]
    else if (j = -1)
             ~~^~~~
cell.c:401:16: note: place parentheses around the assignment to silence this warning
    else if (j = -1)
               ^
             (     )
cell.c:401:16: note: use '==' to turn this assignment into an equality comparison
    else if (j = -1)
               ^
               ==
       :
72 warnings generated.
clang main.o cell.o list.o function.o compute.o -o simp -lm
exctags *.[ch]

文句を言われつつも、コンパイルが完了した。文句は、ほとんど言いがかりと言う事で無視するかな。 (cell.cの中の、set_lvar 中のやつは、直した)

educational Scheme compiler Simple Ver0.17.4 (written by sasagawa888)
Simp> (room)
()
Simp> (car '(a b c))
()
Simp> 1
1
Simp> (define a 3)
a
Simp> a
3
Simp> (exit)
セグメンテーション違反: 11 (コアダンプ)

少し進歩したな。コンパイラの違いで挙動が変わるとな。clangは余り一般的じゃないのかな。 それじゃ、FreeBSDに備え付けのlintで、厳しくチェックしてみるか。

lint

使うのは、おいらも初めてだったりするので、一応manを紐解いてみます。

解説
     lint ユーティリティは、指定された C のプログラムファイルを解析し、バグの
     可能性がある部分、移植性がないと考えられる部分、あるいは無駄なコードと考
     えられる部分の検出を試みます。
         :
     現在 lint が指摘する問題点は以下のものです。到達しない文、先頭から入らな
     いループ、宣言したものの使用しない変数、定数値となる論理式。関数呼び出し
     に関しては以下のような矛盾点が指摘されます。あるところでは値を返すが別の
     場所では値を返さない関数の呼び出し、引数の個数が変化する関数の呼び出し、
     関数側で想定していない型の引数を渡す関数呼び出し、返値を使用しない関数呼
     び出し、返値のない関数の存在しない返値を使用している関数呼び出し。

ふーん、これを高度にすると、皆様ご推薦のHaskellになるのね。(違うってば)

[sakae@secd ~/Simple]$ lint -g *.c
cell.c:
cell.c(15): warning: addr1 unused in function initcell [192]
cell.c(342): warning: function returnbool falls off bottom without returning value [217]
    :
function.c(1621): syntax error [249]
function.c(1747): warning: argument type defaults to 'int': lvar [32]

GNUの拡張機能の一部をバイパスするようにしてチェックしてみた。けど、function.cの1621行目 はエラーだってさ。

    int ret = setjmp(load);

f_loadの中。途中で変数を宣言するのはC言語じゃダメって事なの? C++は許してた? その他は、取り合えず大丈夫そう。(mainの返値がvoidってのは、事前に直しておいた。この仕様は、 widows.hとグルになって、exeを作るのに必要なんだろうな。)

とまあ、lintを使ってみたけど、本来なら、gccを使ってコンパイル時にチェックを行うのが筋だろう。 コンパイルフラグに、-Wall を付ければ、チェックしてくれるけど、大量の警告が出てきて、 ちょっとうざいな。気にしたら胃潰瘍になっちゃうぞと、医者に脅かされそうなので、細かい事に は目をつもむる事にする。

作戦会議

いろいろやったけど、素直にgdbを駆使して追い詰めて行こう。

先週の末は、f_load()中で落ちてた。simpmacros.scmのどこかで可笑しくなってた訳だけど、追うには ちょいと複雑過ぎる。マクロの利用は諦めてて、replまで辿りつくか調べてみる。

replの中をちょいと書き換えて、gdbで追跡し易いようにした。

    28      int addr,rv,mv;
    29
    30      printf
    31          ("educational Scheme compiler Simple Ver0.17.4 (written by sasagawa888)\n");
    32      initcell();
    33      initsubr();
    34  //    f_load(list1(makesym("simpmacs.scm")));
    35      int ret = setjmp(toplevel);
    36
    37    repl:
    38
    39      if (ret == 0)
    40          while (1) {
    41              printf("Simp> ");
    42              fflush(stdout);
    43              fflush(stdin);
    44              S = NIL;
    45              E = NIL;
    46              port = stdin;
    47              rv = read();
    48              mv = macro(rv);
    49              C = compile(mv);
    50              D = NIL;
    51              if (asmflag == 1) {

何の事はない。comple(macro(read())) となっていた所を展開しただけである。

[sakae@secd ~/Simple]$ ./simp
educational Scheme compiler Simple Ver0.17.4 (written by sasagawa888)
Simp> (define a 3)
a
Simp> a
()
Simp> (room)
セグメンテーション違反: 11 (コアダンプ)

Linux側で、一番簡単そうな手続き(room)を実行して、正解を確認しておく。

Simp> (asm #t)
Simp> (room)
(args 0 ldg #<subr room> app stop)
F = 99401
G = 164
S = 0
E = 0
C = 1
D = 0
#<undef>

コンパイル結果は、引数0個、組み込み手続きroomをロードして、実行、仮想マシンを停止しろ って事ね。 後に続くのは、核レジスタが保持してるオブジェクト数だ。

そんじゃ、Linuxでも記念に魚拓(core)を撮っておくか。

[sakae@bang Simple]$ gdb simp
GNU gdb (GDB) 7.4.1
  :
(gdb) b main.c:49
Breakpoint 1 at 0x8048b93: file main.c, line 49.
(gdb) run
Starting program: /home/sakae/Simple/simp
educational Scheme compiler Simple Ver0.17.4 (written by sasagawa888)
Simp> (room)

Breakpoint 1, main () at main.c:49
49                  C = compile(mv);
(gdb) b cdr
Breakpoint 2 at 0x804f891: file list.c, line 35.
(gdb) c
Continuing.

Breakpoint 2, cdr (lis=578) at list.c:35
35          return (GET_CDR(lis));
(gdb) generate-core-file
Saved corefile core.688

気分はsbclの、save-corefile-and-die ですよ。

[sakae@bang Simple]$ gdb simp core.688
#0  cdr (lis=578) at list.c:35
35          return (GET_CDR(lis));
(gdb) bt
#0  cdr (lis=578) at list.c:35
#1  0x0804f9fb in assq (obj=579, lis=578) at list.c:83
#2  0x080507d3 in macronamep (sym=579) at list.c:507
#3  0x08049a50 in comp (expr=582, env=0, code=583, tail=5) at main.c:284
#4  0x08048db8 in compile (expr=582) at main.c:117
#5  0x08048b9f in main () at main.c:49

coreを軽く臨場してみました。ここから、continue出来れば、Lispみたいで面白いと思う んだけど、それはちょいと拒否される。coreを生きかえさせる試みをだれかやってたな。 思い出した、 『リンカ・ローダ実践開発テクニック』(CQ出版社)に載ってた。

ゾンビ復活って、オカルトな世界だなあ。移植の続きは次回に持ち越し。ドッグフードなcoreが、 果たして滋養あるフードに化けるか、やってみるかな。