ようやくInterfaceの連載原稿をあげてひと段落したので、OSC関西に向けてKOZOSの開発を進めたい。


進めたいのだが。。。ちょっと別件でToppersをいじっている。なんとか移植できないかな。


う~ん、KOZOSもっと開発進めたいのだが。やりたいことはいっぱいあるのだけど仕事(本業のほうね)も忙しくて時間が無い。。。


Toppersおもしろいね。こちらは本業ではないのだけれど。


最近、鍋をするときに「レバー」を入れるのがけっこう好きだ。


「レバー鍋」でなく、あくまで「鍋にレバーを入れる」だ。本気でレバーを食べるのでなく、ちょっとアクセントとしてレバーを食べるというくらいの量で。


まあなんか鍋にレバーというだけで臭くなってしまいそうな感じがするけど、あまり安いレバーは避けて、あとは煮詰めないように注意すればそんなに臭くなることもない。。。と思う。ぼくは基本的に臭い食べ物は好きなので、無感覚になってるだけなのかもしれないけど。


あくまでレバーは少量で、メインにはせずに。あとレバー単体で食べると飽きるので、ホウレンソウとか一緒に食べるのがおすすめ。鍋にホウレンソウ入れるときは、緑色のうちに早めに上げて、苦味が抜けないように注意。きれいな緑のホウレンソウは、アブラものとか、味の強いものとかに合うよー。


あとレバーは、鍋に入れるなら鳥のレバーがいいですな。軽くゆでると歯ごたえが美味。ゆですぎないように注意。あと安すぎるレバーは避けたほうがいいかも。



ちょっと挑戦したいことがあって5/15締め切りなのでその準備で時間をとっちゃっててKOZOSの開発が進んでいないのだけど、移植先をもっと増やしたい。


で、移植先のターゲットなのだけど、Interface誌の付録ARM基盤がいいかなーと思っている。なんにせよ安価に手に入るし、資料は豊富(Interface本誌を見ればいい)だし、KOZOSのポリシー(安く、シンプルに)にはいいのではないかと。


最近MIPSのアセンブラを読み書きしているのだけど、やっぱしいろんなアセンブラを読み書きできないといかんと思う。で、ARMとかやってみるのもいいかなーと思う。


あと釣りに行きたい。湖釣りか、川釣り(渓流じゃなく、下流の広い川)が好き。湖畔のオートキャンプ場でクルマとめた目の前で釣りができる、というのが理想。それでだらだらと釣りして、テナガエビとかウナギとか釣って七輪で焼いてその場で食べられるといいなー。もちろんルアーでなくエサ釣りで(なにがもちろんなんだかわけわからんが)。諏訪湖とかいいんだけど、湖畔にキャンプ場無いんだよね。。。


移植編の第6回第7回 で、gccの-pgオプションによるプロファイラ機能を利用して関数呼び出しのログを取る方法を紹介したのだけど、実例で学ぶGCCの本格的活用法 をちょっと調べたら、実は gcc では -finstrument-functions というオプションを付けることで関数の入り口と出口で以下の関数を自動的に呼ぶようにできることが判明。


void __cyg_profile_func_enter(void *func_addr, void *call_site);
void __cyg_profile_func_exit(void *func_addr, void *call_site);

とくに関数の出口に関しては、第7回 では戻り先のアドレスを書き換えることで強制的に_mcount()側に戻しているのだけど、-finstrument-functions ならば直接関数を呼び出すようなので確実だし、CPU依存は無いしでいいことづくめ(第6回第7回 のほうでは_mcount()をアセンブラで書いているので、CPUべったりなのだ)。


実例で学ぶGCCの本格的活用法 のほうはざっと読んだ感じ、gccのオプションは、他にもおもしろそうなのがいっぱいある。

うーん、いろいろ知らんといかんねえ。


(注意)このブログは本家のほうの文章部分のみの転載です.ソースコードの配布,画像などについては本家のほうを参照してください.文章中のリンク先は面倒なのですべて本家のほうに変換してしまっているのでご注意ください.

運転疲れとあと腹が減っているのだが,書きためていたぶんがあるので,前回の続きを一気に書いてしまおう.

前回はプロファイラの -pg の機能を利用して,ログ保存をしてみた.今回はもうちょっと発展させてみる.

前回説明したのだけど,_mcount()から戻る際にはLRの値を設定した上で戻る必要がある.で,前回の実装では,_mcount() 側でLRを設定し,実際のリターンは bctr で行っていた.まあこれはこれでいいのだけど,実はここでblrlで戻ってしまえば,戻るときにLRの値が書き変わることで本来の関数の戻り先が _mcount() 側になるので,関数の先頭と終端で _mcount() を呼ばせることができるのではなかろうか.で,本来のプロファイラでは,_mcount() はそのように実装すべきなんじゃないかと思える.

で,書いたのが次のようなかんじ.

.text
.globl _mcount
.type _mcount,@function
_mcount:
stwu 1,-48(1)
stw 3,16(1)
stw 4,20(1)
stw 5,24(1)
stw 6,28(1)
stw 7,32(1)
stw 8,36(1)
stw 9,40(1)
stw 10,44(1)
mflr 3
stw 3,8(1)
bl logging_begin

lwz 10,8(1)
mtlr 10
lwz 3,16(1)
lwz 4,20(1)
lwz 5,24(1)
lwz 6,28(1)
lwz 7,32(1)
lwz 8,36(1)
lwz 9,40(1)
lwz 10,44(1)
blrl

stw 3,16(1)
stw 4,20(1)
lwz 3,8(1)
bl logging_end

lwz 10,52(1)
mtlr 10
lwz 3,16(1)
lwz 4,20(1)
addi 1,1,48
blr

関数呼び出し時には logging_begin() が呼ばれ,関数の終了時(return文によりblr命令が実行されたとき)にはblrlの直後に戻り,logging_end() が呼ばれるようになっている.

logging_end() の呼び出し時に保存が必要なレジスタなのだけど,PowerPCのABIでは関数の戻り値はGPR3,GPR4で返すということになっている.なのでこの2つだけ保存しておくようにしている(でないと呼び出し元の関数の戻り値が破壊されてしまうことになる).

logging.c は,logging() を logging_begin() に名前変更して,あと logging_end() を追加した.以下のようなかんじ.

void logging_begin(int addr)
{
if (!logging_disable) {
logging_buf[logging_cur++] = addr;
if (logging_cur >= LOGGING_BUF_SIZE)
logging_cur = 0;
}
}

void logging_end(int addr)
{
if (!logging_disable) {
logging_buf[logging_cur++] = addr | (1<<31);
if (logging_cur >= LOGGING_BUF_SIZE)
logging_cur = 0;
}
}

logging_end()では,とりあえずアドレスの最上位ビットを立てて保存することで,logging_begin()によるログと区別できるようにしてある.

ソースコードは以下のような感じ.

では実行してみよう.前回と同じように起動して,logコマンドを実行してみる.

> echo aaa
aaa
OK
> log
00043508 000430e4 800430e4 0004314c 8004314c 000430e4 800430e4 80043508
00043268 80043268 00043508 000430e4 800430e4 0004314c 8004314c 000430e4
800430e4 80043508 00043268 80043268 00043268 80043268 00043268 80043268
00043508 000430e4 800430e4 0004314c 8004314c 000430e4 800430e4 80043508
00043268 80043268 00043508 000430e4 800430e4 0004314c 8004314c 000430e4
800430e4 80043508 00043268 80043268 00043508 000430e4 800430e4 0004314c
8004314c 000430e4 800430e4 80043508 00043268 80043268 00043508 000430e4
800430e4 0004314c 8004314c 000430e4 800430e4 80043508 00043268 80043268
OK
>



おー,0x00043xxx と 0x80043xxx が表示されている.0x00043xxx が関数呼び出し,0x80043xxx が関数の終了に相当する.いいかんじだ.

ところでこれには現状で問題がある.というのは,引数の数が8個を越えるとGPR3~GPR10では渡しきれないので,残りの引数はスタックに積んで渡すことになる.しかし _mcount() 内部では呼び出し元の関数の前に,自前でスタックを積んでしまっているので,呼び出し元の関数は,引数が保存されているスタックを正常に読み取ることができない.なので9番目以降の引数は,本来の値とは異なるおかしな値が渡されることになる.(前回は _mcount() 側のスタックを解放してから呼び出し元に戻っているので,このような問題は無い)

この問題の根本的な原因は,_mcount()側でスタックを確保しっぱなしになっているということだ.これを回避する手段については,前回のように _mcount() から戻る際にはスタックを全解放するしかないが,それをやると2回目の呼び出し時にLRの値が残っていないので,正常に戻れない.

対策としては,以下が考えられる.
  1. 引数は8個までと制限を設ける.
  2. 関数終了時にも _mcount() を呼ばせるのはあきらめて,前回のコードを使う.
  3. _mcount()内でスタック作成時に,ひとつ前のスタックフレームの数バイトをコピーして持っておく(結局のところ引数の数に制限はあるので根本的な解決ではないが,制限を拡張してチューニングすることができる)
  4. LRの値を保存するための独自スタックを別に作る.
きちんとやろうとしたら4の方法しか思いつかないのだけど,これはスタックの計算処理が入るので,mcount()がちょっと複雑になってしまう.まあ現実的なところでは1か2だろうか.

まあこれはそのうちちゃんと考えよう.