Mercurial > hg > CbC > old > device
view Changes @ 128:d497c39add36 args-works
arg.c works (?)
author | kono |
---|---|
date | Thu, 03 Apr 2003 03:04:16 +0900 |
parents | eb4d8975926c |
children | fea1b499d47b |
line wrap: on
line source
Thu Nov 25 17:27:12 JST 1999 subroutine call がない 局所変数もない その代わり、大域変数を多用する 大域変数のスコープは局所的 Fortran の用に局所変数は静的に取る recursion する時には、自分で保存する subroutine call時のレジスタのセーブも局所的に行う それは、ちょっと変じゃない? やっぱりデフォルトのフレームは持ち歩くか? recursive call とそうでないのを区別するか? fp は用意する方が良い 関数定義と同時に、それ専用のfpの構造体を用意する C をcompileする時には、stackを持ち歩く fp = (struct func_state *)stack えっと、どこに代入するの? そういう問題もあるわけね。 じゃあ、fpは特別? それは気に入らないな。static なfpにすれば良いわけね。 func(void *stack) { static struct func_state { static struct func_state *fp; int local1; brahbrah... } func_state; // ここまで hidden func_state.fp = (stack -= sizeof(struct func_state)); } func_state をとってくる演算子があった方が良い? そうね。 func.state ぐらい? fp->local1 みたいなことだけするなら、C と同じになる。 call する時のarguemnt も、 static な func_state に置く stack 上の func_state に置く という二通りの選択肢がある。Cと互換なら、当然、後者。 Recursive なら後者だが、この言語は状態遷移を記述するから、static なものでも良いはず。 Internal function は? あってもいいんだけど... Recursive call する時には、 fp をsaveする必要があるね。 (--(struct func_state *)stack) = fp; call callee(&fp->arg,continuation,stack); call しても、戻って来ないから... continuation は一般的にはcode だから... それは Internal function にするか。 continuation に一般的にcompileする方法を考えないといけないか。 self は必要なわけね? 言語の名前も考えないといかんなぁ。 C からのコンパイラも書かないといけないのか... Mon Dec 13 18:53:04 JST 1999 compiler based で、内部で partial evaluation できる? func をdatabaseとして扱えないなら、それはできない。 しかし、状態遷移としては取り扱える。 func.state func.code みたいな形にしてpartial evaluationすれば良い でも止まるのか? textual でない、中間的なコード表現があった方が良い? <-> interpreter? Prolog ではなんでいけないの? --> Unification が重いから Sat Nov 27 13:50:41 JST 1999 func.state とか作るのだったら、 struct { struct { int i; } state; state *code = { i = i+1; }; } name; みたいな形で、それ自体を構造化すれば? で、代入すると部分評価される。 代入も可能。なるほど。 *name.code; 値はあるわけ? 値は &state でしょうね。 self があれば、 struct { struct { int i; } state, *code = { self->i = self->i+1; }; } name; かな。self = state だよね。 union の拡張もあわせて議論すると... Partial evalutator をセマンティクスや実行系にいれておくことは可能か? byte code とか仮想マシンだったら可能。そうでない場合は? いずれにせよ、構造体のタグのunique性を修正しないとだめだな。 ヘッダファイルはどうするの? Mon Dec 13 18:53:18 JST 1999 library との整合性は? exec sequence では、 (--(struct func_state *)stack) = fp; call callee(&fp->arg); という形でpointerだけ渡すの? それは変だよね。 値渡しにするとすれば、複数の値を渡せたほうが良い。 func(void *stack) { static struct func_state { static struct func_state *fp; int local1; brahbrah... } func_state; // ここまで hidden func_state.fp = (stack -= sizeof(struct func_state)); } の引数自体が、構造体であるべき。 func( struct void *stack ) { static struct func_state { static struct func_state *fp; int local1; brahbrah... } func_state; // ここまで hidden func_state.fp = (stack -= sizeof(struct func_state)); } で、構造体に register storage を許す。 func( static struct argment { register void *stack; register void *continuation; } ) { static struct func_state { static struct func_state *fp; int local1; brahbrah... } func_state; // ここまで hidden func_state.fp = (stack -= sizeof(struct func_state)); } すると、caller の方も、構造体を引数とするのが自然。 call caller( static struct argment { register void *stack; register void *continuation; } arg = {a,b}; ) みたいな。もちろん、この構造体はインタフェースと呼ばれる。 argument は、callee にあった方が良いけど、caller 側にあっても 良い。register なんかは、そう。 caller(interface caller_arg = {a,b,c}) みたいなsyntax かな。 caller->interface = {a,b,c}; *caller->code; を、 caller(a,b,c); と称する。 function には、interface と code と state があることになる。 state にアクセスする時のlockは? protected state? synchonized state かな? もちろん、sequential implementatoinでは、そんなものはいらない。 function { interface: register int a; register struct self self; state: int b; serialized int c; code: b = a; } int にvoid value を定義する。実装は重くなるけど... serialzed の semantics は? もう少しmicro-Cに近く! carring state と static state。 Mon Dec 13 19:42:41 JST 1999 interface に regsiter keyword を使うのは、あまりに 実装よりすぎる。でも、でないと状態にできない? そんなことはないか。やっぱりcaller側のstatic 領域に 直接書き込む? だとCより遅そう。でも、引数に40個とかかかれたら... Wed Dec 15 14:09:49 JST 1999 C と互換にする? goto function(argments); goto *continuation(argments); みたいな感じで。 stackの管理は? どうせ、library との互換はとらないと いけないんだから... local 変数がある場合は stack を動かす。でも、戻す奴がいない。 closure 化するか? return した時の挙動が複雑になる。大域returnするわけだら。 argments をstatic 領域にかきこむ方式だと互換性がとれない。 stack 上の frmae pointer 上にないとダメだから。 両立させるのは無理か? つまり、これだと、呼び出された方の frame semantics は、C と互換になる。だから、stackの直後に frame pointer があると思っている (そうか? ) frame pointer stack pointer に沿って移動した直後に、そこからのoffset で引数を操作することになる。 つまり、それはだめだったことじゃない? つまり、goto だと、 frame pointer は、stack の直後とは限らないから。前の frmae pointer 相対に引数にアクセスしてくれれば別だけどね。 stack に引数を積むのは容認して、goto の場合は、向こう側で stack を畳むってのは? ということは、普通の関数と定義の 方法を変えるってことか。ま、悪くはないか。 すると、goto のsemantics は、C と互換になる。それを受ける 方が異なることをする。それは、なんかおかしいな。それに、 それだと関数呼び出しが軽くならない... ということは、やはり、C のcall は、call funciton で 実現して、その他の呼び出しは、すべて、goto 扱いに する方が正しいだろう。 問題は、この言語の関数をcallされた時だな。dual entry にして、 call の時と、goto の時を区別するか。 func: stack processing func_goto: normal processing ... みたいな感じ。でも、return はないから... このあたりも自分で記述できる言語であるべきだよね。その通り。 つまり、C とのstub も自分で記述すると言うことか。 protocol function { interface c { register "%esp" struct { entry code ret(int); void *fp; } *sp; register "%ebp" void *fp; }; code function_code { fp = sp; sp += sizeof(struct local); struct local *local = sp; local->i = 1; goto *fp->ret(local->i),sp=fp; // return(local->i); }; } みたいな感じでさ。さっすが、アセンブラ。いまいちreturnが汚いけど。 まぁ、return はそのままreturnでもいいけどさ。 あ、これは良いかも知れない。code が複数かけるから。 state 以外は、consitent state であることを保証しない。ってのは? local 変数は使っても良いけど、call/goto の前後で、値を保証しないか... うーん、だんだん炸裂してるなぁ。 だから、レジスタに対するマッピングの記述と、そうでない部分の 記述は分離するべきでしょうね。 全部一辺に実装するわけにはいかないからぁ... Thu Dec 16 13:44:21 JST 1999 lock は状態遷移レベルで実現するのだから、self などを 使ってlockする必要はないはず。 全体の直列化は、状態遷移レベルで、 lock(storage) -> transition みたいな形で記述すれば良い。この当たりを、どのように記述するかは もう少し先送りしよう。 引数はレジスタ渡しにしよう。長い引数は、呼び出し側の領域への ポインタとする。実装を規定しても良い。そうすれば、varargs みたいなものはなくなる。だいたい、なんで、そんなものがいるんだろう? 配列を渡せばいいじゃん。 なので、引数は一つ(or 二つ)に限るという方法もある。 とすると、やはり、前もって静的領域や動的領域を確保することは できない。 この言語では動的領域は自分で確保するわけだから、その点は問題ない。 Thu Dec 16 20:24:55 JST 1999 とすると関数呼び出しは、 # register save # set 1st argument in register %eax # set 2nd argument in register %ecx # set extra aguments in save area # set extra argument pointer in %edx jmp function という形式になるわけね。second を処理するのはめんどくさいから一つ にしよう。 えーと、frame pointer はないけど、コンパイルの手順からすると あった方が良い。しかし、frame pointer そのものをstatic にとるのはまずい。だから、frame pointer がfirst argment ということにする方が正しい。とすると引数は、さらに、その 後と言うわけか。 f(fp,argment) fp を渡すのにさらにargment をレジスタで渡すのはおかしい。おかしいけど、 ま、良いか。 return しないなら、return type の定義をとるのは変だな。 f(fp,arg1,arg2,arg3) とすると、それぞれが決まったレジスタに入って、 多い分は配列にあると思われる。ふむふむ... fp->xx でアクセスすれば、そのまま局所変数になる。全部、配列で 送っても良い。 .set label,value で as は値をセットするようですね。 関数コールの後は戻って来ないから後始末の心配はしなくてよい。 frame pointer を使ったら自分で面倒を見ること。 だと a = atoi(s); みたいなことはできない... 普通のCの定義と交じると間違いやすい。 とすると、struct と同様に、 protocol code interface state を用意するわけね。時間あるのかぁ? とりあえず、register 渡しのfunction 定義とgoto文を実装する。 code name(register "%ebp" void *arg) { goto name(arg); } ぐらいかな? で、first argment が必ずregisterにのるようにしないと いけない。register storage class を入れて、 register "%ebp" void *arg とかするわけね。 ってことは、まず、レジスタを実装しないといけないわけね。 で、stack を使った演算は、一応、そのままにする? それでも動くはず。 式の途中でgotoは使えないんだから、それでいいはず。 で、それから、これを拡張していく。 interface c { register "%ebp" void *arg; } code name(interface c) { goto name(c.arg); // c. は省略可能 } とかね。さらに、 protocol name { interface c { register "%ebp" void *arg; } code name(interface c) { goto name(arg); } code name1(interface c) { goto name(arg); } } などとするわけか。なんと、これが C と共存するわけね。うーん。 Fri Dec 31 11:44:03 JST 1999 code でなくて、別な名前のほうが良くない? segment? action? レジスタ名が入るのは、やっぱりいや。optionalには許す。 interface は構造体のようなものだから... 構造体でいいんじゃない? 構造体の場合は... malloc する? う、うーん。malloc するとして、 いつfree するの? 再入するときには、壊れてていいんじゃない? multi-thread でなければね。 multi thread では、状態は、レジスタ経由または、thread local に持つ 必要がある。static は、だから thread local に持たなくてはならない。 大域変数に割り振っちゃだめ。でも、いまは、やめて interface は、とりあえず、二つまでの値渡しにしよう。 self と arg ですね。 もう少し拡張しやすいコンパイラがいいなぁ。 code name (c,a) struct state *c; struct arg *a; { goto name(arg); } local 変数は? この互換性の問題かぁ。 KL/1 を意識して、interface は heap に置くことにしても良い。 GC は言語に入れておくべきだが、interfaceは machine independent であるべき。だとすれば use/forget みたいものはいるだろう。 でも今のところは考える必要はない。 えーと、 code name (c,a) struct state *c; struct arg *a; { int i; goto name(arg); } の時の一時変数iはどうするの? 基本的にはレジスタ割り当てだけど... 使用させない? んー、大胆な御意見。まぁ、やっぱりheapに割り当てちゃう のが簡単か。でも、どうせ抜ける時にはいらなくなるわけだから... ほんらい、この変数は、次のcallでは必要無くなるのが普通。 とにかく、レジスタ変数は必要なんでしょう? だから、GC と合わせて言語を設計すべきだよね。API を規定して、 異なるGCを選択できるようにする。 Sat Jan 1 22:40:22 JST 2000 とーにかく、 storage class regisgter を実装しよう。 stmode=REGISTER で、local storage とおなじ扱いとする static register? は、ない。 symbol table に storage class をたせば? dsp==EXTRN で判定しているから、 local 変数が36以上あるとおかしくなるぞ? sc は GVAR/LVAR だけど、regsiter は LVAR の特殊な奴だから、 sc に入れるほうが正しいか... Sun Jan 2 01:47:17 JST 2000 register 変数はできました。けど、regsiter を二つ使うと、 一杯になってしまうので、REGISTER6 でコンパイルしないと 結構ひどい。が、register 変数を%esi,%edi に割り当てれば いいか。 Sun Jan 2 04:43:04 JST 2000 で、 code name (c,a) struct state *c; struct arg *a; { goto name(c); } の一時変数無しは実装できます。引数は二つまでね。 .file "tmp.c" .version "01.01" gcc2_compiled.: .text # # code name(c,a) .align 2 .globl code code: .type code,@function # struct state *c; struct arg *a; # { # goto name(c); movl %esi,%esi jmp name _5: .size code,_5-code .ident "Micro-C compiled" う、すごい。 goto 文がめんどくさい。stack をたたんで、jmp すれば よいだけだが.. Sun Jan 2 11:17:50 JST 2000 普通のcallをcontinuation baseにすることができる? Sun Jan 2 20:28:45 JST 2000 goto 文だけど、やはり、一度、expr で生成してから、top level で jump code を生成しよう。 Tue Jan 4 03:32:55 JST 2000 code をtypeにしないと、 code *p; とか書けないね。 int *p(); と同じだけどさ。 main(ac,av) int ac; char *av[]; { goto code1(ac,av); } code code1(ac,av) int ac; char *av[]; { if (ac) goto code1(ac,av); else goto ac(ac,av); } Tue Jan 4 04:56:56 JST 2000 うーん、なんかレジスタにつむ順序が違う これは、adecl がreverseにつむから。 code のretrun やはりcodeはtypeにしないとだめ。 main() { goto code1(); } とかだと、main に戻って来れない。もちろん、code1() 以降で、 return するわけにはいかない。(main の disp をcode1 は知り得ない) goto label をcode1の引数に送れば? main() { goto code1(ret); ret: } これだと、ret がforward labelかどうか分からないけど? code1 中で使う中間変数を stack 上にとるのは悪くない。しかし、それを %ebp 経由でアクセスするということは、main の中間変数を壊すということ。 それを防ぐには、main 中のgoto codeで、%ebp を修正してやれば良い。 (今は戻って来ないので問題ない) code1 のgoto では、戻って来ないから、その必要はない。しかし、 label をcode1 中で渡されると、ちょっと気まずい。 とすると、それは禁止して、main() 中でstackをたたんでからgotoするか? そうすると、無限後退して、結局、帰れないことになる... うーん。 main() 中のlocal code を許せば、それは解決するが.. main() { goto code1(code2); code code2() { return; } } みたいな感じ。でも、そうするとscope rule を変える必要があるので厳しい。 ま、悪くはないけどね。 continuation を明示する方法もある。 main() { goto code1(continuation); } code code1(ret) code (*ret)(); { goto *ret; } かな? call/cc ? label へのgotoを許すのもいいけど、 でも、label を許すと、すごくspagettyにならない? Tue Jan 4 11:47:24 JST 2000 contiunation じゃなくて、return keyword を使おう。 (実際、continuation と少し違うし) type が少し変になるけど、まあ良い。 int main() { goto code1(return); } code code1(ret) code (*ret)(int); { goto *ret(3); } だな。prototype も付けないといけないか。 Tue Jan 4 12:21:44 JST 2000 これだとmethodがすべてstatic になってしまう。dynamic なmethod 呼び出しにするには? dipatcher を自分で作ることになる。かなり めんどくさいが... code method(obj,arg) { } か、あるいは、inline にするか... #define のかわりに inline ねぇ。 これはあとで考えて良い。 Tue Jan 4 14:22:19 JST 2000 main の変数を書き潰すのと、gotgo (*reg)(123) での値は、 register 渡しで、current register にのらないので、 結局、return label は専用に作る必要がある。 Tue Jan 4 18:14:07 JST 2000 stack を継ぎ足して、呼び出す方式を取れば、call by value のregister 渡しを制限する必要は無くなる。 複数の値を返すことも容易だ。 .file "tmp.c" .version "01.01" gcc2_compiled.: .text # # code name(a,b,c,d,e,f) .align 2 .globl code code: .type code,@function # struct arg *a,*b,*c,*d,*e,*f; # { # int g; # goto name(a,b,d,e,f); jmp name _5: .size code,_5-code .ident "Micro-C compiled" おお?! %esp new %esp = old %esp - 12 -4 %ebp-4 = g %esi = a %edi = b %ebp = old %esp 0 %ebp+4 = c code_arg_offset=0 %ebp+8 = d %ebp+12 = e %ebp+16 = f interface は付けよう! というか、 goto name(struct {xxxx}) みたいな感じで良いわけね。どれをregisterにいれるかと言う問題はあるが。 で、どうやってcallすればいいわけ? emit_pushするかわりにpush する? うう、これでは、だめか。code argument の数が変わると、 ebp をいちいち動かすことになる。そこにはold sp があるから そいつもコピーする必要がある。 %esp new %esp = old %esp - 20 %ebp-20 = g %esi = a %edi = b %ebp-16 = c code_arg_offset= -16 ((nargs-max_reg)*int_size) %ebp-12 = d %ebp-8 = e %ebp-4 = f %ebp = old %esp 0 そうか、function からcallする時には、local 変数を書き潰して良い。 # goto name(a,b,d,e,f); %esp new %esp = old %esp - 20 %ebp-20 = g %esi = a %edi = b %ebp-16 = c code_arg_offset= -16 ((nargs-max_reg)*int_size) %ebp-12 = d %ebp-8 = e %ebp-4 = f %ebp = old %esp 0 disp=0 (*) local1 <----16 local variable %edx -12 <- disp_offset %ecx -8 %ebx -4 %ebp = %esp 0 %eip 4 <- arg_offset となる。ということは、pushl %ebp は、間違い。 だけど、%ebp をそのまま使うのは良くない。disp_offset がかかっているから。 だから、もう一度 pushl %ebp したほうがよい。しかし、push する先は、 上の、(*)。 leave movl %ebp,%esp popl %ebp じゃなかったか? Thu Jan 6 13:00:33 JST 2000 できたね。これでとりあえず動くはず。速度は問題だが... あとは、 ANSI-C prototype ANSI-C prototype check Interface Definietion GC support Direct handling of Frame だね。簡単に出来そう? たぶん... Fri Jan 7 09:42:53 JST 2000 goto 文が動いてなかった。あと peep hole optimization version も 作るか? continuation として label を送れるようにするべきか? そうすると便利なんだけど、ちょっと、汚いプログラムが 出来るようになる。あと、送り側の環境(frame)を維持する 必要がある。ま、できなくはないか... そうすると、label が値を持つようになる。 a = label:; とか。うーん。label:(a,b,c) {}; みたいな形で、parallel 代入を許すと言う 手もあるね。 こちらの方がCとの相性は良いが... main() { label:(){ ... } } みたいなnestを許すかどうかと言う問題がある。 変数の参照を許さなければ、特に問題はない。 a = label: は、二重の意味があるなぁ。 言語の名前。DinnerBell II とか? join も入れる? code entry_a().entry_b() {} ですか? parallel call も? Fri Jan 7 19:53:53 JST 2000 いまのままだと return が環境を持ってないから、大域脱出できない。 まぁ、環境を入れてもいいんだけど、どこに置くかと言う問題が あるね。 そうじゃなくて、return 側で判断するか? retrun(ID) みたいな形でIDで判断する。そうすれば、return 側でID を見て判断できる。けど... まぁ、はやり、環境を持って歩く方がいいかなぁ。でも、 引き渡しているから、二つ引き渡して、片方を使われたときに、 反対側が消えてしまうのはいたいよね。今のままならば、 そういうことは起こらない。 continuation 特有の問題を避けるなら、このままでもいいんだが... contrinuation や環境は、このシステムでは自分で作ることが できるからね。 そうなんだけど.... retlabel や retcont は実はオブジェクト 全体に一つあれば良い。 引数を渡すときに、そこに環境へのポインタをいれてやれば良いので、 解決は割と簡単だが、そうすると、例の構造体を引数で渡すと言う 問題を解決する必要がある。 でも、今の実装ならば、まったく同じ変数の構成ならばコピーは 実際には起こらないわけだから問題ないはず。特に、それを保証するために、 interface を実装する必要がある。 return -> (void *)old bp return address bp を直接操作できるようにするといいんだけど.... Sat Jan 8 08:49:59 JST 2000 今は、code 内ではreturnできないわけだけど。実は、return って、 code return0(i) int i; { return(i); } か? 今は禁止してないから書けちゃうよね。どういうcodeが出るんだろう? doreturn() では retpending をセットしているだけだから、control=1 のまま。で、code の終りにくるのでエラーになる。checkret は、 statement の引数で判断するようにしたほうが合理的だろう。 大域脱出は結構根が深いよね。途中をスキップされてうれしいか? destructor と同じで、途中のcodeは全部呼びたいのが普通だろう。 bp が同じになるまで return すれば良いわけだよね。 return と同じで、明示的に環境を引き渡すようにするか。 type は void * で良い? return する時にstackの上限を越えているかどうかを自分でチェックする 必要があるね。誰にreturnするかをcodeで明示すれば良いわけだけど。 code return0(i) int i; { return(i); } を許して、そこで、frame pointer を大域あるいは渡した引数と 比較して処理する? code return0(i,env) int i; env *env; { if (env==self) return(i); } あれ? これはおかしいよね。 code return0(i,env) int i; env *env; { if (env!=self) { env->return(); return(i); } } も、なんか変だよなぁ。呼び出しと逆順に帰りたいわけだが... 実際、逆順には帰っているわけだよね。 return の中でこそこそ比較するという技もあるけど。 問題は、destructor に渡す情報だよね。もちろん、self で良いわけだが、 このあたりは、言語外の問題で、それを明示的にしたいから、この言語を 作っているわけなのだから、これを内部で処理するのはおかしい。 code return0(i) int i; { return(i); } これだと、return の型が合わないと言う問題が生じるな。簡単には チェックできない。 int main() { code a() { } code b() { return(i); } } にすれば、check はできるようになる。でも、これだと、 int main() { a: { } b: { return(i); } } と差がない。module 化を言語外でやるというのが主旨なのだから、これでは まずい。これは高級アセンブラなのだから。 あそうか、return と、大域脱出時のabortとは、状況が違う。だから、 別なcode を呼び出さないとだめ。あるいは、値で区別するか。これは、 logic programming のfail/success と似ている。 main() { } abort { ... } でもいいけど? main() { code abort { ... }; code return { ... }} かな? 本来、subroutine call自体が、かなりの省略形なわけだから、これは 仕方がない。今のままで通常のsubroutine callをシミュレートできるのか? code (struct arg {...},void *sp) { struct env; push(sp,arg); push(env,arg); } できるけど、ちょっと重い。やはり、frame pointer を直接操作しないと だめ。 goto 文のほうに、env を一緒に送るものを作ったほうがいいのかも。 goto (*ret)(),environment; かな。type は? (void *)? goto ret(),environment; にはならないの? そうすれば、自分でthreadを制御できる。environment の正当性を評価しなくて良いの? まぁ、ねぇ。 これは実装は容易だが... goto といちいち書くのが本当にいいのか? env に対するoperationがあった方がいいなぁ。push とか? code return0(i) int i; { return(i); } を認めれば、return 擬変数はいらなくなる。 でも、実は、return は、caller の引数の数と一致してないといけない わけだから、 code return0(i) int i; { return(i); } はだめ。env と一致してないといけない。ということは分離するとまずいんじゃない? あれ? そんなはずないな。 Sun Jan 9 01:15:56 JST 2000 やはり、分離してはまずい。もともと、 goto func(arg); 自体が、 goto func(arg) with current.env みたいなものだ。つまり、これは、DinnerBell の、 self message: arg と同じ。self->func(arg); でも良い。が、function callと区別が付かないのは 良くない。 そうすると、type code はsize int でなくなる。 code *p = func; ではいけなくて、 code p = {func,env}; でないといけない。実際、 goto func(arg) では、current environment を pushl %ebp でstack = current env に積んでいるわけだから。 いずれにせよ、 struct p = q; は実装する必要がある。localな、 code p = {func,env}; も動くはずだが... code (*p)(); goto (*p)(arg); はだから少しおかしい。これは、goto がenv を補っていると考えるべき。 このようにすると、常に、 func,env の組をcodeとみなすことになる。これは、object と呼ぶべきだ。 ただ、既存のobjectとは別だよな。actor の方が良い? うーん、これでjoinを入れれば、完璧なDinnerBellだな。並列送信はないけど。 Sun Jan 9 01:40:05 JST 2000 local 変数の初期化はallocation の後に遅らせる必要がある。 nptr に入れられるはずだよね? nptr に初期化フラグを足すか? 文途中で出現するlocal変数の初期化。ちゃんと動いているの? 構造体のcopyは、lcheck を修正すべきでない。 Sun Jan 9 08:49:43 JST 2000 うーん、なんか修正が多いなぁ。あと、関数呼び出し、goto 文の 構造体への対応か。 goto (*code)(); が、self env を使うのか、code の先の値を使うのかを区別する 必要がある。もし*を使わないとするとlabel(FNAME)との区別が つかないぞ。あ、でも、環境を持ち歩くことにしたから、label へもjumpしようと思えばできるね。 並列送信はなくても、この構成ならばstatement単位の並列性を検出するのは 容易だろう。 やればできるけど、この修正の量だと1日じゃ終らないかなぁ。 不動小数点も入れるのでしょう? Mon Jan 10 09:00:12 JST 2000 引数に構造体を許すには、必ずANSI-Cにする必要がある。難しくは ないが... goto 文には label, code, continuation の3つが来る。 continuation = code + env | label +env なのだが、code/label では、env の内容が異なる。できれば面白いが、 その価値はあるのか? しかし、code , env を分離するとあまりに危険すぎる。どうせgoto が危険なんだからいいか? その方が簡単。簡単なら、そっちの方法を とるべきじゃない? うーん。 return の関数依存性はなくした方が良い。 一つにするのは、pop の問題があるので良くないが... そうか、ret をenvを指定して戻るようにしたから、leave する必要は なくなった。そして、push %ebp に相当する部分は、lea -disp(%ebp),%sp で消去されている。ということは、jump のfunction依存部分はいらない ということだね。 でも、汚いなぁ。read only属性をhardware supportできればなあ。 sched_yeilds() 相当を書けるかな? lock は? 一応、できたけど、やっぱり汚い。 Wed Jan 12 16:12:27 JST 2000 あは。ANSI prototype はめんどい。 bexpr() で、関数での引数の順序と、そのあとの宣言の順序が変わることがある。 そうすると、うしろの方が優先されてしまう。これは、こまる。 そうか、code_arg_offset のような方法だと、ANSI style では、 困ってしまう。 Thu Jan 13 04:46:12 JST 2000 # goto name(a,b,d,e,f); code name { int g; ... %esp new %esp = old %esp - 20 %ebp-20 = g code's local variable %ebp-12 = f <- new_disp %ebp-8 = d %ebp-4 = d %ebp-0 = c %edi = b %esi = a %ebp = old %esp 0 disp=0 new env local1 <----16 old local variable ( to be erased ) %edx -12 <- disp_offset %ecx -8 %ebx -4 %ebp = %esp 0 <- old env %eip 4 <- arg_offset Thu Jan 13 13:38:24 JST 2000 だいたいできたけど、test/tmp7.c のprintf のtype mismatch は なんなんだろう? ASNI の副作用だろうなぁ。 これだと、プロセスの切替えのときには、結構な量のデータを コピーすることになる。それでもいいんだけど... それごと、どっかにとって置く。continuationへの参照みたいなもの ができないかな。 コピーができれば、environment/return の組は動くわけだから、 それへの参照と切替えがあっても良いよね。 Fri Jan 14 12:03:35 JST 2000 Libretto のkeyboardが壊れた... control key が効かない... printf の参照の問題は解決しました。list2 がlocalなheap に割り当てているのがいけなかったね。 return の処理は、goto 文で処理するより、environment に returnto する方が良くはないか? environment は実は送り先でそれなりの準備が必要。 new-environment() みたいなlibrary があれば、thread にできる。 join は? funcall を用意すると良いね。 Mon Jan 17 15:23:34 JST 2000 struct aa f1() { return bb; } みたいなのは? 関数の型か代入の型を見て、crn にpointerを渡して、 あとでcopyしてから stack を畳む。 # bb=f1(aaa); movl $bb,%eax pushl %eax movl $aaa,%eax pushl %eax call main2 popl %edx copy %eax,%edx,$400 addl $400,%esp # return a1; あ、でも、それだと、local変数を返したときに困るね。leave; ret; してはいけなくて... あ、やっぱり、こういう場合はコピー先をmain2に引き渡しているみたいね。 void f1(struct aa *ret) { *ret = bb ; return; } と同じか。これは簡単。 f1().a[55] みたいな場合は、局所変数に強制的に取ってしまうみたいね。それはそうだ... が、うちの実装だとちょっと厳しいか。 leal $-sizeof(struct),%esp pushl %esp なんだけど、関数呼び出しの途中ではできないから.... # main(ac,av) # int ac; .align 2 .globl main main: .type main,@function pushl %ebp movl %esp,%ebp pushl %ebx pushl %ecx pushl %edx # char *av[]; # { # register int i; # register char *p; # int j = 3; # struct { int b; void (*c)(struct aa); } q = {3,main1},r; # # j = 3; subl $20,%esp このsublを後から指定してやればOk。 でも、それだと jump の時にずれない? ずれるか? ずれるね。うーん。 実行時にチェックしてやるのも変だし。 まぁ、それほど必要な機能ではないんだけど。 これもcontinuationを渡してやると言う手法が使えないことはないんだが... 関数呼び出しの最初にやってやればいいか。それでできるかな? Sun Feb 20 23:59:16 JST 2000 MIPS のcall frame $sp = $fp local variables saved register (including $31 = return address) mask は使用したレジスタのbit pattern -4 は何? 18 .mask 0xc0000000,-4 19 .fmask 0x00000000,0 20 0000 D0FFBD27 subu $sp,$sp,48 21 0004 2C00BFAF sw $31,44($sp) 22 0008 2800BEAF sw $fp,40($sp) 23 000c 0000000C move $fp,$sp 24 0010 21F0A003 jal __main 25 0014 03000224 li $2,0x00000003 # 3 26 0018 000082AF sw $2,a 27 001c 04000224 li $2,0x00000004 # 4 28 0020 00C082AF sw $2,b 29 0024 05000224 li $2,0x00000005 # 5 30 0028 000082A3 sb $2,c 31 002c 06000224 li $2,0x00000006 # 6 32 0030 08C082A3 sb $2,d 33 $L1: 34 0034 21E8C003 move $sp,$fp # sp not trusted here 35 0038 2C00BF8F lw $31,44($sp) 36 003c 2800BE8F lw $fp,40($sp) 37 0040 0800E003 addu $sp,$sp,48 38 0044 3000BD27 j $31 39 .end main これと同じようにするならば、regiterの使用数を最初に調べる必要が あるのだけど、one path compiler である micro-C では、それは できない。したがって、enter は後ろでする方が良い。 Mon Jan 20 18:25:27 JST 2003 3年間さわってないのかよ。何やってんだ? goto 文のバグをとらないといけない。 まず、関数引数の構造体の展開。これは、どうってことないはず。 goto (*code)(i+1,j,...) まず、いじらなくてすむ変数を摘出する。 foreach arg compare 単純演算 ( op+const , pointer , const assign ) などは、ここで検出する。 大半は、そのようになるはず。 レジスタに乗せる分があるから... それには触らないとして... 複雑なものは、前もって計算しておく。(get_register する) スタック上かレジスタ上に作る。 残りは並列代入となる。再帰的に計算する。 えーと、大きな順にやるんだっけ? 小さな順にやるんだっけ? code f( int a, int b, int c ) { goto g(b,c,a); } みたいなやつだよね。 移動するものを一つ検出する。 そのために移動が必要なものを移動しておく(再帰) 代入する こんなんでいいのか? ループしない? するよね。ループしたら get_register する。 前の例だと、 g(b,c,a) のbに着目する。 bに代入するコードを出すと、a が壊れる。 a が必要かどうかを調べる。それは、引数のリストを見ればわかる。 その前に、a を移動する。a の移動先を見て、 空いていれば、移動してOk。しかし、c なので、 c を見る。と b になるので、ループするのがわかるので、 b を get_register する。 で、c が移動できる。で、aを移動して、とっておいたbを代入。 Tue Jan 21 22:45:09 JST 2003 とりあえず、jump は複雑すぎる。もっと簡単にすることを考える。 parser 側である程度処理できない? goto f(a+3,b(),c); などを、 a = a+3; b = b(); goto f(a,b,c); 程度に簡略化する。この時、f(a,b,c) は(できるだけ)、元の 関数の引数リストに近付ける。のは無理なので、単純変数 まで落す。 あまり関係ないか。一時変数はどうせいるわけだし。ってこと みたいね。 だとすると、元のコードと、そう変わらんね。前のも、そんなに 悪くないってことか。 Wed Jan 22 14:33:12 JST 2003 やっぱり、途中で局所変数を増やしたいよね。 Fri Jan 31 20:30:36 JST 2003 なんか #ifdef / #if がないとだめだな。実装する? しました。 Tue Feb 4 01:04:12 JST 2003 ## while ((*chptr++ = c = getc(filep->fcb)) != '\n') { _1120: movl $10,%eax movl $8,%ecx movl filep,%edx addl %ecx,%edx movl (%edx),%edx pushl %edx xchg %edx,%eax .... edx に$10が入る (なんでxchg?) call getc addl $4,%esp movl %eax,-20(%ebp) c に代入 movl $chptr,%ecx pushl %ecx popl %ebx movl (%ebx),%ecx ecx にchptrの中身 addl $1,(%ebx) movb %al,(%ecx) subl %edx,%eax eax-edx ($10) je _1119 が壊れる理由なんだけど... edx,ecx が破壊されちゃうみたいね。 Tue Feb 4 12:17:07 JST 2003 ようやっと直したよ... use_pointer って、なにもしなくていいんだよね? eax,ebx を避ける ってことらしいけど。 inline/引数付き #define 欲しくない? 置き換えは、local name stack に積んじゃう。 展開は function で行う。 getch を工夫する必要はあるが。置き換えスタックが必要。 Wed Feb 5 01:16:00 JST 2003 大域で定義された struct field が大域変数と重なっていると落ちる。 そりゃそうだけど。どうするの? (直した記憶があるんだけどなぁ...) struct 毎に field 名とoffset/type の組を持てばい良いんだよね。 なんだけど、タグ無しの構造体もあるから、型名の方に付ける必要 もある。というのは、型名のない構造体もあるから。タグ名には、 一応、リストがついている。なんかに使う必要があったんでしょう ね。あ、めんどう。無条件にやっても大域変数名を汚すのを直すの が難しい。 ちょっと、あれだけど、「型名.フィールド名」で登録してしまうのはどう? 型名が後で出て来るところが気まずいが... def で登録するときに、nptrにdispを代入せずに、struct field list (大域変数) に入れて、type の方に、field list (list3(nptr,offset, type)) を入れれば良い。 あとは、strop の方でtypeのlistを見るようにすれば良いわけだ。 これなら、簡単に直せるはず。LUSTR/GUSTRなどの区別もなくなるし。 Wed Feb 5 02:10:14 JST 2003 浮動小数点ねぇ。完全なANSI Cにするのは大変。でも、 浮動小数点ぐらいないと。 code generation part を、さらに分割して、 複数のコード対応にしやすいようにする。 おそらく、それほど共有する部分はないけどね。 Sample C code をコンパイルして、その結果から(半分手動で) Micro CbC code generation part を生成する方法を用意する。 Thu Feb 6 11:47:03 JST 2003 Code Segement を単位として使うときに、大域変数はどういう ように分けるの? static なんかは意味ないよね。 もちろん、自然にグループ分けされるわけだけど。 あとデータフローだよね。データフローに関しては、 あんまりやってないなぁ Fri Feb 7 14:36:15 JST 2003 inline では、必らず、局所変数の増加がある。また、inline は普通の関数として展開しておく必要もあるらしい。(何故?) #define ねぇ。 #define c(a,b) g(a+1,b+1) #define g(a,b) printf("%d %d\n",a+1,b+1); main() { int a,b; a =1; b = 3; c(a,b); } local #define がいるんだよね。g の中で a が出て来た時には、 c のa の置き換えは起こってはいけない。ということは、c の置き換えはg が始まる前に終っている必要がある。dynamic scope なんだから、assoc の上乗せで良いはず。 macro のlevelを定義して、あるレベルでは、それ以前の展開 を行わないという手法が良いかな。 c(a,b) => a=>"a+1", b=>"b+1" g(a,b) => (a=>"a+1+1",a=>"a+1"), (b=>"b+1+1",a=>"a+1") みたいな感じ? やっぱり関数解析でマクロ処理をやらせるのは無理かな? 先読みされちゃうし。 Sat Feb 8 00:53:52 JST 2003 macro は途中まで書きました。置き換えをマクロが呼び出された 時点で cheap に置くと、それを解消するタイミングがない。 ここだけmallocしても良いが.. chptrsave はlistにする必要がある。list で良い。 やっぱりmacro levelを見て、自分と一致したassoc valueまで 手繰って置換するんでしょう。そうすれば、置き換える必要は無い。 ということは、local_define にmflagsを格納する必要がある。 c(a,b) => a=>"a", b=>"b" a=>"a" .. mflag == 1 g(a,b) => (a=>"a+1+1",a=>"a+1"), (b=>"b+1+1",a=>"a+1") a=>"a+1" .. mflag == 2 macro のもとのnptr が残ってないと、オリジナルを返せない。オ リジナルは、sc などが破壊されてしまう。ってことは、local macro は、local table を汚してはいけないってことだよね。ってことは、 macro table は、もとのとは別に用意する必要がある。 #define c(a,b) g(a+1,b+1) #define g(a,b) printf("%d %d\n",a+2,b+2); main() { int a,b; a ... local a =1; b = 3; #ifndef a c(a, a = "a".. macro mflag==1 g(a, a="a+1" mflag==2 ^ a = "a" mflag==1 While replacing a in g's body, a should not be replaced to (original) "a", should be c's a. b); /* 3,5 expected */ #endif } うーむ。ややこしい。 main() c(a,b) mflag++ a=>"a" mflag ==1 g(a,b) mflag++; a=>"a+1" mflag ==2 ^ is replaced by c's "a" not g's a; いったん mflag level n で展開したら、それは mflag level n-1 となる。 Sat Feb 8 18:13:43 JST 2003 いちおう、mflag まではデバッグしたが.... mflag を戻してないんじゃないの? ---c(a,b)----------------------- mflag ==1 a=>hoge, b=>hoga (mflag==1) ----g(ac,bc)----------------- mflag ==2 ac=>goge, bc=>goga(mflag==2) ----printf(a,b)---------- mflag ==3 a=>poge, b=>poga(mflag==3) g が呼び出されると、ac,bc は mflag==1 でのみ置換される。 あるテキストを置き換えると、それは置き換えたマクロのmflag level (つまり一つ少ないレベル)になる。 置き換え終ったら、元のlevelに戻す。 mflag==2のlevel では、mflag==2のlocal macroの展開しかしない。 置き換えると、mflag level 1 になるので、そこで mflag==1 のlocal macro を展開する。mflag==0 は常に展開を行う。 Sun Feb 9 11:35:23 JST 2003 うーん、なんかtypeが、list とCHARなどと入混じっているじゃん。 int save = chptrsave; で、chptrsave が、$chptrsave になってしまう。 Sun Feb 9 22:33:36 JST 2003 #define car(e) (heap[(int)(e)]) #define cadr(e) (heap[((int)(e))+1]) car(cadr(e)) だろ。 car -> #define e cadr(e) (mleve=1) cadr -> #define e e (mleve=2) むぅ。これ、うまくいかないんじゃん。こまったなぁ。 #define c(a,b) g(a+1,b+1) #define g(a,b) printf("%d %d\n",a+1,b+1); c(a, b); こっちもだめじゃん。ふーむ。lisp interpreter のように 作ればいいはずなんだけど。 Mon Feb 10 08:10:25 JST 2003 結局、list base のinterpreter を実装しました。きちゃないが。 前の方法でも、頑張ればできるんでしょうけどね。 Tue Feb 11 13:50:03 JST 2003 struct copy だけど... 関数がstructを返すときに、引数に前もって 積んでおくのでは、そこに値がコピーされてしまうし、あとで、 スタックをたたんで置くときにきまずい。 function call の時に、引数の型のチェックをしてない type に -1 とheapの引数が混在しているやつだけど.. やっぱまずいんじゃないか? temproal struct は再利用できるんだけど、dispの変更ができないので 新しく作るしかない。大きいときだけ新しく作るなんていうセコイ 技はあるけど。(そうすると、帰って来た値へのポインタを使えなく なるが.... 別にいいよね。戻り値それ自身を直接 return する 時もだいじょうぶなはず) 結局、呼出側で、領域を確保して引き渡すことにしました。この方法だと、 代入のときに二度コピーする必要もない。 register を使用しているかだけじゃなくて、実際にcreg/dregに 値があるかどうかを記憶する必要がある。 Wed Feb 12 11:09:22 JST 2003 それだけどさ... やっぱりアドホックに実現するのは難しいんじゃないの? まぁねぇ。register の場所の確保と、寿命は別だから、それで いいんだけど、regs flag だけでなんとかならないのかな。 こういう変更ははまるが虚しい。 Thu Feb 13 18:37:36 JST 2003 さて、そろそろ jump にとりかかりますか。 構造体の引き渡しのシークエンスに使う局所変数の位置がgccと違う... そろそろ register は構造体にすべきだね。 struct register { int used; int valued; char *name; char *wname; char *bname; int type; /* register variable or not */ int number; } virtual/real は、どうする。 Sat Feb 15 14:00:03 JST 2003 fdecl_struct を構文的に引数が出現するときに行うと、int *f(int a) などで、* の評価が終る前に、int aが評価されしまう。*obj のobj を評価し終らないとfのタイプが確定しない。int*f()[] み たいな場合があるから。(?) なので、gcc と、そろえるためには、 arg の先頭で fdecl_struct を行う方法ではだめで、fdecl 中であ とから修正する方が良い。 fix しようにも引数リストなんて、存在しないじゃん! varargs を実装するのはめんどくさかろう... rvalue(expr(),type) では、expr() のtypeをrvalueに引き渡せな い。でも、type を大域変数にすると、rvalueを異なるタイプで呼 び出すときにtypeを変更する必要がある。このrvalueのtype の扱 いは、かなりはまったことがあるので、rvalue(int e,int type)の 方が良いことは確かなんだが... struct_push のregisterの扱いが複雑すぎ。なんか、もっと 簡単にならないの? Sun Feb 16 07:58:23 JST 2003 代入しなくて良いからと言って、ソース のリストから除いては、上書きを防げない。 Sun Feb 16 22:55:58 JST 2003 vdisp ってなんだったんだ? Mon Feb 17 12:35:39 JST 2003 並列代入は出来たみたい。代入は小さいものを先にすべきなのか? まぁ、できりゃいいんだけど、横に避けるものが大きいのはいや だよね。 Tue Feb 18 11:56:10 JST 2003 overraped 用の emit_copy float/double long long Tue Feb 18 19:34:31 JST 2003 code argument の符号を反転させると、list2(LVAR,offset) のoffsetがアドレスの方向と一致しているという前提が 崩れる。それで、構造体の格納順序がずれてしまう... ということは... def(n) でcodeの時はargumentは、局所変数と同じ 扱いでマイナス符号で処理した方が良い。 できたみたい。でもさ、 int main( int ac, char *av[]) { int n; goto arg1(0,1,2,3,4,return,environment); } って、きっと return 文がないと、文句を 言われるよね。むむむ。 post processing する時にoverrapしてないという保証がない。 Wed Feb 19 15:38:55 JST 2003 自分自身とのoverrrapを見てないので、 struct a a,int i int i,struct a a みたいな時に自分自身を壊してしまう。なので、emit_copy が、ちゃんと方向を見て壊さないように処理する必要がある。 call bcopy でいいじゃん。まね。 Wed Feb 19 20:42:07 JST 2003 楊さんの C2CbC と CbC2C を、micro C に取り込む。各所に、 conv->func(); を埋め込む。conv は、構造体。 conv: original c2cbc cbc2c とする。なるほど。 Thu Feb 20 05:34:58 JST 2003 むぅ。code-ia32 で結構はまった。あと、stack のアライメントが ずれるみたい。6809では問題にならなかったんだけどね。 leave で調整するべき。 Thu Feb 20 14:42:46 JST 2003 c2cbc,cbc2c なんだけど、いったん、構文木にしてから変換すると、 結構失われる情報があるけど、いいの? 特に局所変数の名前とか型 の情報とか。macro もそうだけど。 indent ぐらい保存できないか なぁ。式の途中でfunction callする場合も取り扱う必要があるの で、構文木にしてから計算するしかないかな。gexpr の代わりに生 成するようにするか。そうすると、修正するのは、statement と、 gexpr になる。でも、結局、構文木で型を持ち歩くしかないんじゃ ないの? やっぱり変だよ。型の情報がないのは。 そうではなくて、exprN() で変換していく方法もある。この方が 情報が欠落しないので楽だろう。そうすると、修正するのは、 exprN(),doXXXX() となる。これは、量は多い。けど、まぁ、それだけ。この方が オリジナルを保存しやすい。 中間変数を途中で追加すると、宣言部を前もって出力できなく なる。{int a;...} を認めれば良いわけだど。実装は難しくない。 じゃ、やれば? でも、汚くなるなあ。出力をいったんバッファ に貯めれば? どこに? cheapp ですか? 中間変数はいらないん じゃないの? a = g(i)+a; でしょ。 goto g_1(i,f_1,save_a); } code g_1(i,f_1,save_a) { .... goto f_1(ret_value,save_a); } code f_1(ret_value,save_a) { a = ret_value+a; ...} じゃん。いらないじゃん。。 型を表示するルーチンが必要だね。 めんどくさいなぁ。CbCのProlog version とかほしいなぁ。そうすれば、 変換は簡単。でも、やろうとしてできなかったことでもあるな。 Thu Feb 20 21:13:23 JST 2003 MC6809 の mc-codegen.c version は? (ちょっと虚しすぎる?) X と D で、use_data_register, use_pointer してやる。 tosop で、X と D の間の足し算を特別扱いする。 (なるほど...) MAX_MAX=0でうまく動くのか? やっぱり、1は いるよね。できれば、2だよね。 結構、浮動小数点も簡単かも。 汎用の書き換えツールも便利そう。 でも、Prolog version ってのも面白そう。 code(name,interface(....)) :- p()....,goto(name,interface(....)). みたいな感じ? 結構、簡単にinterpreterを書けるかも知れない。 これは、あれだね。thread diagram interpreter と似ている。 Fri Feb 21 13:34:17 JST 2003 構文要素での書き換えだけど、どれくらいの能力があるの? その場での書き換えだけだと、ちょっと低すぎない? それで、 cbc2c,c2cbc はできるとは思う。 まぁいいけど.. chk を無視しているところが結構あるんですけど。 jmp,enter,leave ... Sat Feb 22 13:05:04 JST 2003 type の印刷かぁ... conv 系は大半はdefault convが呼ばれる。なので、c.c っていうよりは、default.cだよね。必要なところだけ、 自分で代入すると言う方法が良いだろう。その方が ヘッダも一つで済むし。もちろん、object 指向なら 簡単なんだが... Sun Feb 23 19:47:41 JST 2003 struct のtypeを印刷するんだけど、一回印刷したら、あとは印刷 しない方が良い。逆に毎回書くなら、tag名type名は、いらないの か。 とするとtypeの解釈はやめてndeclで処理する? 印刷では、それで いいわけだけど。 このタイプの印刷だと再帰型は印刷が終了しないんじゃないか? しないね。 Mon Feb 24 02:31:06 JST 2003 strings の\nなどを元に戻す必要がある。 なんか括弧がわやになってるな。 Mon Feb 24 11:02:07 JST 2003 typedef されたタイプは、そちらを使う方が良い。けど、情報が失 われてしまっているので、どこかにとっておかないとだめだね。dsp ? うーむ、これはなかなか難しい。全サーチしてもいいんじゃないか な。遅いけど。少なくともgnptrで定義されたものはサーチすべき でしょう。 indirect function type の表現がなぁ.... sdecl ではconv を行うので、type_print ではsdeclを経由した場 合に表示を行ってはいけない。 なんか関数の引数の型の値が微妙に変わるんですけど... やだなぁ... まだ、関数定義のtypedefがstructに変わってしまう。gtypedefed がうまく動いていない? Mon Feb 24 17:24:31 JST 2003 まぁねぇ。やっぱり完全に構文木から再構成した方が 便利ではあるよね。特に getsym (空白など)と conv->x_() との総合作用はめんどくさい。 そのためには、局所変数名をtree上で持ち歩く必要がある。 まぁ、そうすれば良いだけだけど。 実際、今のセットで出来るかどうかは、ちょっと怪しい。 たぶん、buffer に出力するってのをいれればおそらくは 変換できるだろう。 式の途中での呼び出しとかを考えると、やっぱり 構文式から生成しないとだめだろうね。 (ってことは、まだ、かなりの作業があるってこと.... むぅ...) tmp2.c は、通らないし... Fri Feb 28 20:32:46 JST 2003 で、c2cbc は、途中で float の方を先にやるわけ? Sat Mar 1 22:05:43 JST 2003 creg_destroy は、ぜんぜんだめ。これは基本的なアイデアがだめ。 long long はstructでいいんじゃない? だめ? で struct 演算を別に 定義してやる。これは、実装にもよるか。 float,long longなんだけど、 FRGVAR DRGVAR LRGVAR などを作る。さらに、 FMUL DMUL LMUL などもいる。型の変換は binop で解釈する。変換も演算になる。 D2F, D2I, F2D, F2I, I2D, I2F, U2D, U2F ぐらいですか? emit_pushは、型を必要とするけど? そうだねぇ。emit_fpush, emit_dpush かな? creg にfloat register(or stack) の値を入れればいいんじゃないの? それを見て、emit_pushの型を決める。creg は、けっこう、いろんあ ものが見ているので、いじらない方がいいじゃないかなぁ。 そうね。FMULとかがあるなら、それで判断できそう。 構文木には型を含めないってのは不便。型を入れれば? そうすれば、 RGVAR CRGVAR FRGVAR DRGVAR LRGVAR ではなく、 RGVAR ですむし。その方が変形も楽だしね。型は、 CHAR,UNSIGNED, UNSIGNED CHAR, INT, FLOAT, DOUBLE, LONGLONG ぐらいですか。 emit_data とすると、書き換えが結構あるけど。 Sun Mar 2 12:58:38 JST 2003 あとはconstantだね。FCONT,DCONSTかな。binopでは 変換とかも必要なわけだけど。 そういえば、shot のload/storeもないね。SRGVAR,SASSとかですか? SASS は、すでにあるなぁ。 Sun Mar 2 22:01:58 JST 2003 あれ? conv->_sm(); (*conv->_sm)(); の場合は、 *conv->_sm の値へcallする んだけど、 goto exit1(); goto (*exit1)(); の場合は、 exit1 の値へjumpするんだよね? およ? なんか勘違いしてる? なんでexit1() だとindirectが出て、(*exit1)だと出ないんだろう? この宣言は、 void (*_sm)(); であって、 void _sm(); はできない? なんで? あぁ、まぁ、いろいろ、めんどくさい。 やっぱり、arglist の再帰的扱いがちゃんとしてないとだめ。 うーむ、 enum なんてのもあるのね。やさしいけど、いるのか? code (code *) * みたいなのがあるので、conv は手直しが必要。 Mon Mar 3 12:38:08 JST 2003 float/duble は順調に進んでるけど、3日はかかるでしょう。 binop を書いちゃうとmc-parse.c は、ほとんど終り?! 代入と関数呼び出しが残っているか。あと single もあるね。 emit_push base で書くんだけど、他のCPUではだいぶ様相が 違うんだろうな。 dreg/creg のfloating versionが必要です。( です? ) Tue Mar 4 14:56:28 JST 2003 double のcurrent register は387のスタックを使う。(?) 関数呼び出し時に387のスタックが保存されるという 保証は無いので、emit_dpushでは、%esp に保存する。 でも、そうすると、代入の後などで387のスタックが 常に残ることになる。こいつをクリアするコードは どこにいれる? free_register でもいいんだけど.... ううーむ、つかいずらいやつ。ld じゃなくて、 stack top に代入するオペレーションはないの? (あぁ、でも、なんか、floatは、もうすぐ終っちゃうな... なんか、 さびし...) Tue Mar 4 23:58:07 JST 2003 fmulp とかでは、fpp のstackのつじつまはあう。fstl とかだと、 合わない。fstpl すればいいんだが、そうすると連続代入でまずくなる。 値を使うかどうかを代入時に知ることができれば良いんだが。 (use flag で判断することにした) 結局、freg は、使わなかったね。0が、入っているので直さないとまずいか。 fregを見て、stack を直すってのは、やっぱり、まずいよな... でも、浮動小数点レジスタを持つCPUの場合はいるんじゃないの? Wed Mar 5 11:25:11 JST 2003 printf では 引数はdoubleで統一する必要がある。goto() では、 それをするのはまずい。局所変数と同等だから。ってことは、 関数の引数はdoubleにしないとだめなのね。プロトタイプが ある時は、その限りでない。(うーむ...) (くそ、こいつの副作用が結構出るな...) あと、fpp のスタックに頼っているので、overflowした時に 困らないか? ほとんどの場合でだいじょうぶだが、だめだった 時にerrorぐらい出せば? でも実行時にしかわからないか... I2D でさ、singned/unsigned の区別がいるね。 やっぱり FCONST いるんじゃないの? Wed Mar 5 19:51:59 JST 2003 副作用以外は終ったけど.... あと、name space が結構重なっている んだよね。 struct tag, struct field が重なっているのは結構うっとうしい。gsearc/lsearch のあたりも 書き直さないとだめかも。 あと、list もなぁ。mode の作り方にもよるんでしょうけど。 あと、結構、汚いよな... Wed Mar 5 21:15:34 JST 2003 できたよ。まだ、テストしてない部分はあるけど。局所変数の初期化とか。 FCONST とか。3日で出来たね。 (gcc の方をやった方がいいかなぁ...) goto.c が通らなくなってるな。 Thu Mar 6 14:00:13 JST 2003 PowerPCのnon-lazy pointerって、テーブルに入っていてそれを読 み込む形式なのね。いったん、レジスタに読み込んだら、それを再 利用する形が良いらしい。ということは、code_gvar にキャッシュ を作らないといけない。しかも RLU ですか? (めんどくさ〜) でも、そうしないと、 i = i+1; なんてのでも、ひどい目にあってしまう。 local variable も最初に move mutilple register で大半は レジスタに読み込んじゃうみたいね。pointer で参照されると 困るんでしょうけど。まぁ、31個もあれば、そういうことを してもあまり問題ないのかも知れないけど。 creg を破壊しない実装にすれば、少しはましになるんじゃない? (IA32では、それは難しいけど) Thu Mar 6 20:50:24 JST 2003 やっぱり、使った分だけregisterを保存するようなコードに なるみたい。one path で、それをするためには、 .align 2 _main0__: lwz r5,2136(r1) addi r1,r1,2128 lmw r23,-36(r1) mtlr r5 blr .align 2 .globl _main0 _main0: mflr r2 stmw r30,-8(r1) stw r2,8(r1) li r11,0 stwu r1,-480(r1) li r2,100 mtctr r2 mr r30,r3 addi r0,r1,64 mr r9,r0 b _main0__; とかいう感じにするしかないね。 あと引数は、レジスタに積むようになっているみたいだけど... r3 から? mflr r31 li r0,7 stw r0,56(r1) だから8個まではレジスタに積むみたいね。 r3-r10 だね。 構造体もregisterにコピーされるのか。そして、向う側で受取の コピーを行うわけだね。先頭にreturn structへのポインタがあるのも 同じ。 (うう、memcpyしまくりだ...) 浮動小数点もレジスタ渡し見たい。f1から。(なるほど) saveFPってのを呼び出して、 f24-f31をsaveするらしい。 (31-24)*8 数合わないな? pointer のことを考えると、レジスタだとまずいものもある わけだけど。アドレスを取られてからで間に合うんじゃない? 配列などは、もともとだめだし。場所と値(?)は確保するとして。 そうか、逆に、r3-r10 は引数でなければ壊しても良いわけか。 (それでr9とか良く使われているわけか...) ということは足りなくなったら、引数をセーブすれば良いわけね。 (ってことは、LVAR かつ REGISTER っていう状況があるんじゃん... まぁ、そうだけどさ...) これは get_register がめんどくさそ。 どうも、r11-r12 あたりは自由に使っているらしい。 引数を作っている途中で関数呼出しするときは、どうするの? r30などに移動するのか? なんか知らんけど、in file call name と out file call name が 違うみたいね。(sigh...) こんなのなんとかなるのかなぁ。 全部、stub にしておいて、.set で書き換えるという手もあるけど。 (さすがに一日ではできないか...) なんか整数から浮動小数点への変換はじぶんでやらないとだめなのね。 これはサブルーチンを呼んだ方がましだ。 get_register は絶対失敗しないようにできるんじゃないか? label があると、code_base cache はclearしないといけない。 それを判断するには fwddef をhookする必要があるけど。 Fri Mar 7 09:17:10 JST 2003 問題は、 register allocation と function call/goto call の構成だな。goto の方は machine dependentなところは ほとんどない。register のsaveさえ必要ないから。 register allocation だけど。 r0 r1 frame pointer (or stack pointer ) r30 jj r31 relocation register r0,r1,r2 システムで使う r0-r10 引数 引数でないところは優先的に使う r20-r29 セーブして使う領域 r10-r19 はどうなんだろう? セーブしないのか? ia32 の方でもfppのスタックを関数呼び出しのときに吐き出した方が 良い。 r0 r1 r2 r3 r4 r5 r6 r7 r8 r9 r10 r11 r12 r13 r14 r15 r16 r28 r29 r30 r31 なので、もののみごとに、17-27 までが使われてないね。 ということは、関す呼出し時には、保存されるレジスタはないと 思った方が良いってこと? あるいは、r17-r28 は保存されると 思って良いのかな。 Sat Mar 8 19:28:42 JST 2003 関数呼び出し時のレジスタセーブを避けるためには、関数呼び出し を優先して実行してやれば良い。関数呼び出しの結果は局所変数に セーブする。 f(g(h(a)+1)+2) は、 a1=h(a) a2=g(a1+1) f(a1+2) となる。そうすれば、関数呼び出しのときのスタックはかならず0になる。 でも、結局、引数は関数呼び出しの前にセーブするのね。だったら、 そんなことしないで、セーブすれば良いのか。 Mon Mar 10 11:42:40 JST 2003 で、レジスタのセーブなんだけど、mc-codegen.c を変更しない とすれば、引数のリストを使って変更していくのが良い。 関数呼び出しは基本的には並列代入になる。並列代入でき てもできなくても、セーブする必要はある。今の並列代入 ルーチンのできは良くないので、「同じかどうか」だけ 判断するのが良いのではないか? 関数呼び出しの前に式用にレジスタに積まれた値はセーブした 方が良い。セーブした後はlvarとしてアクセスすることになる。 スタックでもいいけど。 そうすると、stack 配列には、 レジスタ レジスタ番号 (>0) スタック -1 lvar lvar番号 の三種類が入ることになる。それに合わせてtosop/assopを書き直 す必要がある。emit_pop だけでいいかも。スタックを止めちまう のも手ではあるが、ia32の方で効率が悪い。やっぱり三種類サポー トするのが良いだろう。 (けっこういろいろあるなぁ... どこから手を付けるか...) Tue Mar 11 14:23:57 JST 2003 save_stacks すると、レジスタはほとんど使われなくなって しまう。が、コードの見通しは良くなる。 定数を右辺に持って行くとsave_stacksでsaveする量が減る。が以 外にめんどくさいね。反射律が成り立たない演算子に関しては。tosop の定数版とか作ることになるので... あとスタックに積む順序が逆 になってしまう。まぁ、もとの版では行われていたことだが... CMPではrexpr で反転して論理を逆転させる方が簡単か。SUBでは、 ADD + (-CONST) にする方が良いね。 je _xxx したあと、current register のregv[]が残ってしまう。これは、 変だよね。gexpr_init で regv が残るのは、case 文の比較だけ。 あとは全部0にして良い。(まぁ、害は無いんだけど) (case 文のjumpは、switch 文が終る時に処理すれば、index jump にすることができるね) Wed Mar 12 12:58:47 JST 2003 比較で入れ換えるとの否定は若干違うよね。 Thu Mar 13 19:39:48 JST 2003 そういえば、doif で条件が定数だったときとかの最適化は してないんだね。やさしいけど。chk を使えば良いので。 f(g(1,2,3),g(1,2,3),g(1,2,3)) とかだと、結局、g の返り値は一旦メモリに入れないとだめじゃん。 なんだけど、実際は、r29-r22 を使っているようですね。 ってことは、function call の時に、r3-r10 が前の引数かどうかを チェックして、引数だったらr29-r22に移す作業がいるわけだよね。 いったんr3とかに入れてしまった後だと、重複してしまうが... 前もって関数呼出しがあるかどうかは、調べることができるから、 関数呼び出しがあったら、そうするようにする? いろいろめんどくさいなぁ... (いったい、いつなったらできるんだ?) Fri Mar 14 10:38:25 JST 2003 function の dsp が、arglist と extern flag の両方を入れてしまっている。 本来は sc で区別するべき物だよね。 なんか、emit_pop_free のxregがLVARの時のバグを取るのに苦労した... gdb は top level でのwhile を受け付けなくて、define してやなんないと だめみたいね。 Fri Mar 14 15:50:28 JST 2003 なぁ... 書いても書いても書いても、終らん! Fri Mar 14 19:43:44 JST 2003 jump の中で input register を割り振るときに floating point register の ことを考えてなかった。これは register_var とは異なるので異なる 仕組みで割り振る必要がある。ってことは、get_input_register_var が要るってこと? 0x9001a544 <saveFP+4>: stfd f15,-136(r1) 0x9001a548 <saveFP+8>: stfd f16,-128(r1) 0x9001a54c <saveFP+12>: stfd f17,-120(r1) 0x9001a550 <saveFP+16>: stfd f18,-112(r1) 0x9001a554 <saveFP+20>: stfd f19,-104(r1) 0x9001a558 <saveFP+24>: stfd f20,-96(r1) 0x9001a55c <saveFP+28>: stfd f21,-88(r1) 0x9001a560 <saveFP+32>: stfd f22,-80(r1) 0x9001a564 <saveFP+36>: stfd f23,-72(r1) 0x9001a568 <saveFP+40>: stfd f24,-64(r1) 0x9001a56c <saveFP+44>: stfd f25,-56(r1) 0x9001a570 <saveFP+48>: stfd f26,-48(r1) 0x9001a574 <saveFP+52>: stfd f27,-40(r1) 0x9001a578 <saveFP+56>: stfd f28,-32(r1) 0x9001a57c <saveFP+60>: stfd f29,-24(r1) 0x9001a580 <saveFP+64>: stfd f30,-16(r1) 0x9001a584 <saveFP+68>: stfd f31,-8(r1) 0x9001a588 <saveFP+72>: stw r0,8(r1) 0x9001a58c <saveFP+76>: blr なのか。ということは、 f%d に対して、"saveFP+%d",68-(31-f)*4 かな? うーん、これで、全部書いた気がする。でも、ほとんど全部の 関数にバグがあるだろうなぁ。一つ一つ取っていくしかないか。 Sat Mar 15 16:04:09 JST 2003 やぁ... まぁ.... バグだらけだな。 function call のレジスタの処理がでたらめ。RISCの場合は、parallel_assing した方がいいんじゃないか? Sun Mar 16 20:59:10 JST 2003 あぁ、まだまだ、かかりそうだ.... 定義されてない関数/定数をextern扱いにする必要がある。(なんで、PICなの?) function は c0(n->ty)==FUNCTION で識別する。n->sc には、FUNCTION/EXTRN[1] が入る。だよね。(CODE)も。だけど、n->ty に戻り型が入っている場合が けっこうあるらしい。 (しかし、これは、結構かかるな...) Mon Mar 17 12:02:22 JST 2003 function のnptrだけど、 nptr->ty function の return type list3(FUNCTION,return_type,arg_type_list); nptr->sc 本来はFUNCTION/CODE (EXTRN/EXTRN1) nptr->dsp 引数のリスト と言う構成なわけだよね。で、引数のリストとtypeは重複している。 他のnptrとの整合性を考えると、 nptr->ty return type nptr->sc FUNCTION/CODE nptr->dsp 引数のリスト が良い。で、引数のリストにEXTRNの情報を入れる方がいいんじゃないか? (どっちにするんだよ...) プロトタイプと実際の引数リストの整合性はチェックしなくちゃ いけないわけだから、別な方がいいんじゃないか? とすると、 やっぱり前者か... extern と、そうでないものとの呼出しを、呼出しの時点で 区別しないといけない。しかし、prototype で定義されている ものと default extern の区別は、最終の時点でしか判別できない。 できないよね。定義されてないものが default extern なんだから。 ってことは、最後に、.set で定義するしかないか。(sigh...) Mon Mar 17 14:34:12 JST 2003 えーと、input register に regv/regs をセットしないとだめ。 関数呼び出しの引数を評価する前に save する必要がある。 さらに、引数の評価の後に、save された変数を呼び出す必要が ある。(ってことは、いままでのは、まったくのでたらめか..) register 変数の場合は、問題ない。ってことは、ia32 側も 変更してしまったので、おかしくなっているね。もっとも、 code の場合は、そういうsaveとかは必要ないから良いのか。 (11日目か...) mr creg,hoge mr hoge2,creg とかは、g_expr_u で最適化するべし。set_freg/set_creg でレジ スタ変数に割り振ると、set_freg でfreeされてしまう。 浮動小数点定数の共有はやった方が良い? input register のsaveを忘れている。 input register の割当が逆順。 Mon Mar 17 23:38:14 JST 2003 あぁ、そうか。input-register のアドレスを取ったときは、 それをLVARに変えないとだめ。 なんだけど、途中で分かっても、loop で前に戻ることがあるので、 手遅れです。ということは、one path ではできないね。 むぅ... function call の save_input_register も同じ「手遅れ」 の問題があるのね。function argument は、すでに parse されて いて、その引数は、register に固定されてしまっている。 save_input_register で、save するコードを出しても、 そちらを見るようには出来てない。(どうすればいいんだ?) function callの先頭で、引数を全部stackにsaveしてしまえば、 このあたりの問題は解決する。けど、あんまりな気もするね。 でも、stmw も使えるしな... 結局、input_register は、LVAR のまま処理して、可能ならば register を使うって方がいいんじゃないかなぁ.... でも、 そうすると、今まで書いたコードは、ほとんど無駄かぁ... いずれにせよ、あんまり簡単な解決はないね。 Tue Mar 18 10:46:48 JST 2003 結局、全部 stack にsave しました。そうすれば、変更は1行。 やっぱり、関数全体を構文木に落してからgexprするべきだよね。 それ自体は、そんなに難しくないが。 Tue Mar 18 12:41:42 JST 2003 COND(三項演算子)で使うレジスタが関数の引数を破壊してしまう。 input register にならない値を使えば良いんだけど。r15 を使っているんだけど、それでいいの? virtual を使っていたときは、use_register で何も破壊されなかった んだけど、使わないとすると、結構破壊されてしまう。なので、 ちょっと奥の深い問題かもね。三項演算子の型はチェックしてなかったし。 まぁ、それは、一時レジスタを使えば良いとして... 時々呼び出す、u2d とか bcopy とかで破壊されるinput レジスタ はどうする? これは、予測は困難だよね。使う分だけsaveするって 言う手もあるけど。うー、どうしよう... 検出できるようにするっ ていう手もある。(と、思ったら、get_register_var が間違ってい るみたいね) 検出はちょっと重いか。二乗のアルゴリズムは、あま り使いたくない。といっても、定数乗になるだけか。呼出側でsave する? bcopy がどれくらい使うか割りと曖昧。 もっと積極的にget_register_var するっていう手もあるよね。そ うすれば、あまり気にすること無く library call できる。(ちょ っと、書き換えが多いが...) その方が筋かな。使うレジスタを減 らそうと思うと、それは検出するのと、それほど差はないわけか。 このあたりも構文木を全部持っていれば解決できるんだよな。 うーん、まだまだ、だな。 関数が関数呼び出しで決まる場合の input register の破壊を考えて ませんでした。これも、どうすればいいんだ... まぁ、get_register_var するのが簡単だよね。 save register が狂っているようだね。max_register_var あたり? Wed Mar 19 02:22:51 JST 2003 なんだか、struct のtag listが、int a,b,c; のようになっているのを 一つと数えているらしい。困ったものだ。前に言ったのをちゃんと 実装しよう。 macro のバグも見つけちゃったよ... むぅ。 でも、とりあえず、a.out は動きました。 Wed Mar 19 12:59:01 JST 2003 なんか、浮動小数点をprintfに引き渡すときは、rnレジスタにも 値をいれていて、しかも、そっちを見るみたいね。これは、 めんどくさい.... r4,r5 ... f1 r6,r7 ... f2 r8,r9 ... f3 f4 からは単独 (あぁ、なんか、これは異常にめんどくさいぞ... こんなことするなら f0 から r0 にコピーさせてよ...) 40(r1)とかをprintfは、派手にぶっこわすようですね。いったい どれだけ取れば良いんだろう? -112 ぐらい? 今は-72程度か。 (なんか風邪ひいた... 家内がくるというのに...) 浮動小数点のバグも順調に取れています。 なんか、save_stack が余計なsaveしてない? まぁ、一時変数を良く使うのでfree listを作った方が良いかもね。 その前に整数演算のチェックが必要だな。 Thu Mar 20 12:06:27 JST 2003 まだ、save したレジスタを破壊しているな。 set L_98,244 stw r15,lo16(-44+L_98)(r1) ぐらいで、もう壊れちゃうみたい。ってことは、L_98 がもっと 大きくないとダメなのか。 Thu Mar 20 23:43:42 JST 2003 ようやっと、self compile が通りそう。register save 系が やっぱり難しいみたいだね。dynamic loader を壊したりするし。 あとmodがおかしいみたいで、hashの値が違うらしい。マイナスの値 になるunsignedのかけ算ねぇ。int.c のrecursionが通らない。 まだ、offset がおかしいらしい。 macro のバグもとれたし。 一応は、self compile は通りました。 Fri Mar 21 03:18:26 JST 2003 やっぱり、r1 と x(r30) の計算が合わない。きっと、 呼出側で、引数の分のoffset を用意しているのだろう。 だから、どれくらいの引数の関数を呼び出したかという 値がいるのではないか? Fri Mar 21 12:22:11 JST 2003 <------r1_offset------------------------------> <------------lvar_offset-------> r+ +------------+---+---------------+----------+--------------+----+ - caller arg xx register save local callee arg xx reg_save disp max_func_args*size_of_int ということだったみたいね。ようやっと self compile が通りました。 free_glist を作るか.... Sat Mar 22 11:17:41 JST 2003 creg/freg を g_expr の引数にした方が、レジスタマシン系では、素直な みたい。そうすれば、 ## conv->static_(); addis r15,r31,ha16(_conv-L_242) la r15,lo16(_conv-L_242)(r15) lwz r3,0(r15) lwz r3,244(r3) mr r29,r3 mtctr r29 みたいな mr は減るね。もっとも一命令だけどさ。3% もあるみたい。 ちょっと多いか... 386 での use_register もなくなるしなぁ。 (あぁ、でも大半はポインタキャッシュだな。こっちを直す方が 簡単か) まぁ、そういう最適化をしないっていうのが、このコンパイラの立場 なわけだけど。 構造体の引数渡しでは、構造体そのものをレジスタに載せて 引き渡しているみたいだね。まったく... それで、あわてて メモリに代入しているわけか。 なんか mtctr r2; bdn Lxx とかいうのがあるのね。main frame っぽい! これはベンチマーク用って感じだね。 Sun Mar 23 16:06:29 JST 2003 Breakpoint 3, get_register () at mc-code-powerpc.c:235 235 for(i=MAX_TMP_REG;i>MIN_TMP_REG;i--) { (gdb) c 9999 Will ignore next 9998 crossings of breakpoint 3. Continuing. test/basic.c:67:Bug of compiler Breakpoint 2, errmsg () at mc-parse.c:214 214 if(lineno==0) return; (gdb) info b Number Type Disposition Enabled Address WhatStackFrame Condition IgnoreCount Commands 2 breakpoint keep y 0x0000ca08 in errmsg at mc-parse.c :214 breakpoint already hit 1 time 3 breakpoint keep y 0x000028e4 in get_register at mc-code-powerpc.c :235 breakpoint already hit 69 times ignore next 9930 hits (gdb) run The program being debugged has been started already. Start it from the beginning? (y or n) y Starting program: /Users/kono/src/device/mc -s test/basic.c [Switching to process 24650 thread 0x2307] test/basic.c:67:Bug of compiler Breakpoint 2, errmsg () at mc-parse.c:214 214 if(lineno==0) return; (gdb) c 69 うまくいかんね。 a+a+a....a で落ちてしまう。まぁねぇ。 結局、stack frame の問題か。 Mon Mar 24 03:06:32 JST 2003 Intel 側にもだいぶ embug してしまったようだ。 basicとかfloatの難易度を上げすぎたか? mc-code-power.c のlvar は、 input arg > 0 local var < 0 output arg > ARG_OFF とするべきだね。 Mon Mar 24 12:08:43 JST 2003 addi r27,r1,56 addi r29,r28,28 mr r3,r27 mr r4,r29 li r5,372 bl L_memcpy$stub lwz r4,0(r28) lwz r5,4(r28) lwz r6,8(r28) lwz r7,12(r28) lwz r8,16(r28) lwz r9,20(r28) lwz r10,24(r28) mr r3,r28 bl _main3 うーむ、最低な奴。最初の28byteだけレジスタ渡しなのか。アドレス は変わらないんでしょうけど。まぁ、合わせなくても害は無いけどさ。 そのうちね。(そんなに難しくは無いが... また、function が複雑 怪奇になるな) しかし浮動小数点レジスタも使うとかそんなんじゃなくて 良かったかも。 stwu r1,lo16(-L_9)(r1) とかしているから、局所変数は64k以内ってことだね。haしてもいいんだけどさ。 Mon Mar 24 17:07:59 JST 2003 さて、ようやっと fact-a.c に取り掛かれるわけだけど、今のままだと、 input register -> メモリへのsave ってのがあるんだよね。これはいただけない。sub routine call すると、 r0-r10 は破壊されちゃうし。ということは、code segment の引数は、 r29-r20 が良いんじゃないか? 浮動小数点f31-f20を含めて。 問題は、どこでinput register を指定するかだけど、get_input_register_var で code 用かどうかを指定するのが良いかな。fnptr ではダメなので明示した 方が良いだろう。 r31, r1 の設定があるのはやむを得まい。r1=r30であった方が良いのだろうか? その方がfunction calll の時の変更が少ないだろう。 メモリとinput register (この場合はr29-r20,f21-f20だけど)の対応は どうする? 通常だと呼出側が確保するわけだけど、そうはいかないね。 レジスタもsaveする必要が無いから、いろいろ簡単でよろしい。 あとは、return/environment の扱いだけど。ia32 の方でもいろいろ 動かなくなっているようだな。 Mon Mar 24 21:56:47 JST 2003 うーん、別にフレームを変えないで、そのままでいいか。 その方が楽だよね。return しようと思うといちいち frame をうごかさないといけないけど、return は 別処理するんだから、いらないか。 Wed Mar 26 14:29:21 JST 2003 で、フレームを変えないとすると、 <------r1_offset------------------------------> * <------------lvar_offset-------> r+ +------------+---+---------------+----------+--------------+----+ - callee arg xx register save ! local caller arg xx reg_save disp max_func_args*size_of_int lvar>0 lvar<0 lvar>0x1000 0000 なので、r1/r30 を常に移動させる必要がある。前のcode segement/ function がどこにr1/r30を持っていたかを知る手段はないので、 jump する前に糢とに戻す必要がある。r30が固定ならばr1のみの 修正で良く、前に戻す必要はない。 でも、r1_offset 分を戻せば良いだけなんだから、r30を * に固定 しても良いんじゃないか? ! でもいいんだけど。こっちの方が簡単 かな? code の場合は register_save は 0 (つまり、マイナス)な わけだから。(まぁ、穴を開けておいても良いけど) Intel の場合はどうして、こうしなかったのか? (でも、最後にチェック した時にはcore dumpしていたが...) ebp <-----esp-----------> <------r1_offset------------------------------> * <------------lvar_offset-------> r+ +------------+---+---------------+----------+--------------+----+ - callee arg xx register save ! local caller arg xx reg_save disp max_func_args*size_of_int lvar>0 lvar<0 lvar>0x1000 0000 というわけなので、* にそろえる方が intel 版とも一致する。 ってことは、ebp の移動は intel でもやっていたってこと? いやfunctionではこうなっているんだけど、code では違う。 ebp <-----esp-----------> <------r1_offset------------------------------> * <------------lvar_offset-------> r+ +------------+---+---------------+----------+--------------+----+ - callee arg xx register save ! local caller arg xx reg_save disp max_func_args*size_of_int lvar>0 lvar<0 lvar>0x1000 0000 ってわけか。とすると powerpc では、 r30 <------r1_offset------------------------------> r1 # * <------------lvar_offset-------> r+ +------------+---+---------------+----------+--------------+----+ - xx callee arg zz register save ! local caller arg zz reg_save disp max_func_args*size_of_int lvar>0 lvar<0 lvar>0x1000 0000 の#にr30を固定してr1をずらすことになる。やっぱ、こっちだよね。 zz はから。xx に、base となる関数呼び出しが来る。から、 calle arg は破壊される。 どっちも可能だな。両方実装する? やっぱり後者からかな。gcc は 前者の方が簡単だろう。 入力変数をレジスタに割り振ってしまうと、その範囲では 簡単に動いてしまう。(そりゃ、そうだ...) goto with environment で構造体を返すのには注意が必要。 どこでコピーする? goto した場所? return-continuation? そうねぇ。return-continuation 側が普通でしょう。あぁ、 でも返し先は元の関数呼び出しの引数上だから壊れちゃって いるかもね。だから、とっておく実装の方が良いわけね。 (なんか -O3 にすると、mc1 が落ちるな...) Sat Mar 29 16:35:18 JST 2003 r30 <------r1_offset------------------------------> r1 # * <------------lvar_offset-------> r+ +------------+---+---------------+----------+--------------+----+ - xx callee arg zz register save ! local caller arg zz reg_save disp max_func_args*size_of_int lvar>0 lvar<0 lvar>0x1000 0000 このままだと、* が previous r1/r30 になるので、function から goto する時に戻り番地やzzにあるはずのsaved r31/r30が破壊されて しまう。それは、ちょっと困るということは、一旦、dummy subroutine (普通はstub っていうか)を呼び出す方が良い? 戻り番地とprevious r1/r30 は持ち歩いているわけだけど... また、r20-r29はつぶされてしまう ので、前もってsaveする必要もある。f31-f20も。(結構多いな...) # * r30 <--------------r1_offset-----------> r1 r+ +--+----------+----------+-----------+----------+----+ xx zz reg save !callee arg!code local caller arg xx r20-r29 これだと environment は持ち運ぶ必要はない? いや、ある? 逆に戻り番地を持ち運ぶ必要はなくなるわけね。そっちの 方がきれいかな。goto-cotinuation みたいな感じか。 返り側は、 lwz r1,continuation lwz r1,0(r1) lwz r0,8(r1) mtlr r0 lmw r30,-8(r1) blr 程度で良い? このr30ってのはgoto先では知り得ない。いや、待てよ、 この場合は、r20-r29って決まっているわけか。 lwz r1,continuation set r3/f1 as a return value lwz r1,0(r1) lwz r0,8(r1) mtlr r0 lmw r20,-148(r1) (?!) blr で良いわけか。構文は同じでreturn addressは無視することにするか。 呼出側は、 通常の関数呼び出し (引数0) (は、手間0だから..) bl L_stub_NN L_return: (普通のretrunもあるから、必要な場合がある) lwz r1,0(r1) lwz r0,8(r1) mtlr r0 lmw r20,-8(r1) blr L_stub_NN: mflr r0 bl L_61 L_65: lwz r1,0(r1) lmw r23,-108(r1) b restFP+36 ; restore f23-f31 L_61: stmw r23,-108(r1) stw r0,8(r1) b saveFP+36 ; save f23-f31 ちょっと複雑すぎるかな。特に gcc では、こういう操作は難しいかも。 あ、そうか。register saveは、このy関数が呼び出された時点で やってしまえば良い。そうすれば必要ない。戻り番地も既に xx に入っている。だよね。 * gotoを呼び出した関数のr1 ! r1(goto前のr1) # * r30<--r1_offset-------------> r1 r+ +----------+--+----------+----------------+-----------+----------+----+ cousin arg xx reg save !callee arg !code local caller arg xx r20-r29 lvar>0 lvar<0 lvar>0x1000 000 f20-f31 <-my_func_args--><--disp-----><-max_func_arg-> *size_of_int *size_of_int とすれば stub は必要ない。cousin arg も保存されている。goto の 引数のみがlocal変数を上書きする形で格納される。これで、original のIA32実装と一致することになる。(引数の保存を除いて。いや、 元のも保存されているんじゃないの?) それで、code segment から関数呼出しするときは? (ってことは、 それようのテストも必要なわけか...) Wed Apr 2 17:40:32 JST 2003 register 変数が既に使われていても、わざわざsaveする必要はない。 戻って来ないから。register を使ったことにするには、used_max_register_var を設定してやれば良い。 Interl版で goto が動かなくなったのは、arg_offset が、register 変数分 も取るようになったから。save することを考えると、その方が良い。実際、 function call があるとsaveされてしまう。ということは、別に r20-r29 である必要はないってことか。通常のinput register varで良い。とすると、 register変数をsaveする必要もないね。 ただ、input regsiter var は、function callの前には saveする必要が ある。でも、これは、やっぱり手遅れか。ってことは、やっぱり、 r20-r29が良いってことか。 jump() では、呼出し引数は、-arg_size つまり、局所変数と 同じ扱いになっている。ってことは、 * gotoを呼び出した関数のr1 ! r1(goto前のr1) # * r30<----r1_offset---------> r1 r+ +----------+--+----------+----------------+-----------+----------+----+ cousin arg xx reg save !callee arg !code local caller arg xx r20-r29 lvar >0 lvar<0 lvar>0x1000 000 f20-f31 <-my_func_args--><--disp-----><-max_func_arg-> *size_of_int *size_of_int r1 <----------------------r30 (return continuation) で、callee arg は保存されたまま、code local だけ変わるってことだね。 return continuation で、構造体を返そうと思うと難しい。そもそも、 浮動小数点が、ちゃんと返るのか?