K.Maebashi's BBS

ご自由に書き込んでください。雑談も可。
テスト書き込みの類はテスト用掲示板にどうぞ

[日付順表示] [日付順インデックス] [スレッド順インデックス]

新規投稿 | 開設者ホームページへ戻る | ヘルプ

[1351] その後のVM
投稿者:
2009/06/11 22:03:54

 ようやく下記のようなプログラムがVMで実行可能になりました。可変長 パラメータ用の書式は作らず、一部の組込み関数のみ可能にしました。 言語としての美しさは…、スタック情報も大きく変わり、VMはほとんど別の ものになりつつあります。VMの完成にはまだまだです。printfはCのprintf と同等ですが、中身はエミュレートを被せてオーバラン等の危険は一切 ありません。 ---------------------------------- int printf(string str); string sprintf(string str); int print(string str); int main() { string str; printf("printf P1 = %d P2 = %d P3 = %d\n",12,20,30); str = sprintf("sprintf P1 = %d\n",13); print(str); print("---------- test ---------\n"); } ----------------------------------  前に出した、組込み関数とのインターフェースは大幅に変更となり。 下記のようになっています。 //== スタック上の組込み関数データ =================================== struct NFstartDat { ushort vtype; // データタイプ E_StackDType ushort size; // スタック消費サイズ ushort m_vmid; // VMID = スレッドID ushort m_prm_inno; // 引き渡されたパラメータ数 void *m_sdatp; // スレッド別データ用ポインター S_Value *m_prmp[PRMMAX]; // 引き渡されたパラメータ S_Value m_return; // 関数からの戻り値データ }; //=================================================================== // 組み込み関数用定義 //=================================================================== class C_NFBase { protected: //-- VMから受け取る情報 ---------------- int m_fid; // 関数ID //-- VMに送る情報 ---------------------- char *m_name; // 関数名 int m_prmno; // 要求するパラメータ数0-10まで20は可変関数 ES_ValueType m_type[PRMMAX]; // 要求するパラメータの型 ES_ValueType m_return_type; // リターン変数タイプ public: C_NFBase() { this->m_name = NULL; this->m_prmno = 0; for(int i=0;i<PRMMAX;i++) { this->m_type[i] = ES_NON; } //----------------------- this->m_fid = 0; this->m_return_type = ES_INT; } virtual ~C_NFBase() { } virtual void * VMstart() { } // VM起動時実行 virtual void VMend() { } // VM終了時実行 virtual void funcexe(NFstartDat *nfd) { } // 関数起動メソッド inline char ** getname() { return &this->m_name; } inline int getprmno() { return this->m_prmno; } inline ES_ValueType getprmtype(int no) { return this->m_type[no]; } inline void setfid(int fid) { this->m_fid = fid; } inline ES_ValueType getrtntype() { return this->m_return_type; } };
[この投稿を含むスレッドを表示] [この投稿を削除]
[1352] Re:その後のVM
投稿者:
2009/06/11 22:46:28

 追記で、バイトコードも結構変わっています。下記に。 >---------------------------------- >int printf(string str); >string sprintf(string str); >int print(string str); > >int main() >{ > string str; > printf("printf P1 = %d P2 = %d P3 = %d\n",12,20,30); > str = sprintf("sprintf P1 = %d\n",13); > print(str); > print("---------- test ---------\n"); >} >---------------------------------- ↓  バイトコード ↓ 1:*** 一般関数情報ダンプ ****************** 1:--- int main() 1:*** ローカル変数の表示 no = 1 *** 1: 0:string str 1:*** 文字列 数 = 9 *** 1: 0: "main" 1: 1: "str" 1: 2: "printf P1 = %d P2 = %d P3 = %d " 1: 3: "printf" 1: 4: "sprintf P1 = %d " 1: 5: "sprintf" 1: 6: "print" 1: 7: "---------- test --------- " 1: 8: "print" 1:*** 使用予定のスタックサイズ = 240 1:*** 関数コードのディスアセンブラ size = 65 1: 0 push_string_const 2 1: 3 push_int_1byte 12 1: 5 push_int_1byte 20 1: 7 push_int_1byte 30 1: 9 call_function 3 0 0 4 1: 18 pop 1: 19 push_string_const 4 1: 22 push_int_1byte 13 1: 24 call_function 5 0 0 2 1: 33 pop_stack_string 0 1: 36 push_stack_string 0 1: 39 call_function 6 0 0 1 1: 48 pop 1: 49 push_string_const 7 1: 52 call_function 8 0 0 1 1: 61 pop 1: 62 push_int_1byte 0 1: 64 return 1:*** 行情報 数 = 6 *** 1: 8: from 0 size 19 1: 9: from 19 size 17 1: 10: from 36 size 13 1: 11: from 49 size 13 1: 13: from 62 size 2 1: 12: from 64 size 1 1:*** end of main() --------------
[この投稿を含むスレッドを表示] [この投稿を削除]
[1353] Re:その後のVM
投稿者:(ぱ)こと管理人
2009/06/11 23:40:18

おお、私はDiksamのほうにさっぱり手を付けられないでいる間に、 だいぶ進んでいますね。 ぱっと見で目を引くのは関数呼び出しのインストラクションのオペランドの 数の多さですが、 >1: 9 call_function 3 0 0 4 おそらく4つ目のオペランドが引数の数、2つ目か3つ目が関数のインデックス なのだと思いますが(確か分割コンパイルはなかったと思うので、ひとつ余りますね…) 最初のオペランドは何でしょう? 関数名を文字列のコンスタントプールで保持していて、そのインデックスに 見えますが、用途がよくわかりません。リンク用の情報で、実行時には使わない? >1:*** 文字列 数 = 9 *** >1: 0: "main" >1: 1: "str" >1: 2: "printf P1 = %d P2 = %d P3 = %d >" >1: 3: "printf" >1: 4: "sprintf P1 = %d >" >1: 5: "sprintf" >1: 6: "print" >1: 7: "---------- test ---------
[この投稿を含むスレッドを表示] [この投稿を削除]
[1354] Re:その後のVM
投稿者:(ぱ)こと管理人
2009/06/11 23:44:05

自己フォロー。 >おそらく4つ目のオペランドが引数の数、2つ目か3つ目が関数のインデックス >なのだと思いますが(確か分割コンパイルはなかったと思うので、ひとつ余りますね…) >最初のオペランドは何でしょう? 2つ目と3つ目のオペランドは全部0, 0なのですが、関数の方はprintf, sprintf, printの 3種類ありますので、これが直接関数のインデックスということはないですね。 単なる場所取りで、実行前に第1オペランドの文字列を使ってインデックスを検索して ここに埋め込むとか……
[この投稿を含むスレッドを表示] [この投稿を削除]
[1355] Re:その後のVM
投稿者:
2009/06/12 20:27:59

>2つ目と3つ目のオペランドは全部0, 0なのですが、関数の方はprintf, sprintf, printの はい、一つ目は文字列バッファーにある関数名インデックスを示し、二つ目はコン パイル単位カウンター、三つ目は関数ID=インデックス、四つ目はパラメターの数 です。P2,P3は実行時に埋め込まれます。コンパイルカウンターは、1~最大値ま でのループカウンターで動的コンパイル実行時かコンパイル単位の消滅処理時に +1されます。コード上は最初は0そしてカウンターは1なので、値が違うためP1の 関数名から動的リンクをしてP2に1、P3に関数インデックスが埋めこめられ、この 関数インデックスで直接関数が起動されます。以後、P2のコンパイルカウンターが 一緒なら、P3で直接関数起動です。直接起動の前にIF文が一回入る程度です。  このような感じで、スレッドセーフな動的リンクを実現しています。実はこれには 面白い副作用があって、例えば画面に文字を出す関数を動的コンパイル単位にすれば。 メインは一切い変更せず。あるときはゴシックで表示、コンパイル単位の消滅そして、 最コンパイルすると、その時から丸文字表示で文字が出される等が可能です。  関数の内部機能を動的に変えることが出来ます。もちろん総てのスレッドにスレッ ドセーフで。でも、使い道はそんなに無いかもしれませんけどね。^^; そうそう、今の所同じ関数名でも違う文字列IDですが、暇が出来たら同一IDにする 予定。 VMを作り始めてから何度もも構文処理からの修正が必要になり。なかなか前に進み ません。書きあがって動いても、スレッドセーフの問題点が見つかり、書き直しも ごそごそと。言語としては最終的なつめなので、すぐには終わりそうにないです。  でも、思った通りに作れるのはいい感じです。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1356] Re:その後のVM
投稿者:
2009/06/12 20:46:52

 追記、変数名とか関数名も文字列バッファーに入れています。これは言語のIDE サポートのためです。今デバックのために使っているモニターシステムがあるの ですが、これを高機能にすればIDEになるので、言語デバック用に簡単なIDEをと 思い追加しています。このデバックモニターシステムはC#で作られています。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1357] Re:その後のVM
投稿者:(ぱ)こと管理人
2009/06/14 01:49:31

>はい、一つ目は文字列バッファーにある関数名インデックスを示し、二つ目はコン >パイル単位カウンター、三つ目は関数ID=インデックス、四つ目はパラメターの数 >です。P2,P3は実行時に埋め込まれます。コンパイルカウンターは、1~最大値ま >でのループカウンターで動的コンパイル実行時かコンパイル単位の消滅処理時に >+1されます。コード上は最初は0そしてカウンターは1なので、値が違うためP1の >関数名から動的リンクをしてP2に1、P3に関数インデックスが埋めこめられ、この >関数インデックスで直接関数が起動されます。以後、P2のコンパイルカウンターが >一緒なら、P3で直接関数起動です。直接起動の前にIF文が一回入る程度です。 うーん、いまいちわかりませんが、 ・call_functionのふたつめのオペランドを「P2」として、 ・「コンパイルカウンター」は、「動的コンパイル実行時かコンパイル単位の  消滅処理時に+1」というくらいだから、VMにひとつだけ存在するカウンタですよね? つまり、動的コンパイルが動いたり、逆にバイトコードを破棄したりするたびに VMの状態が変わるわけですが、この「状態」に対して、1から始まる連番が振られて いるわけですね。 で、call_functionごとに、それがリンクされた時点の連番(コンパイルカウンター)が 埋め込んであり、呼び出しごとに現在のコンパイルカウンターと比較して違っていたら 再リンクする、と。 この方法だと、コンパイル単位が増えてきたとき、プログラムの端のほうでちょろっと 動的リンクが発生しただけで、それこそprint()みたいにあちこちで使われる関数の 呼び出し箇所すべて(通るところだけですが)に対し再リンクが必要になるような…… ちなみにDiksamでは、バイトコード中の関数インデックスはコンパイル時に 決めています。これは「バイトコードごと」の関数インデックスであり、 ver.0.2~0.3系列では、リンク時に、これを「DVMごと」のインデックスに置き換えます。 ver.0.4では、置換は行わず、変換テーブルをバイトコードごとに持っています。 今のところ動的なバイトコードのアンリンクは実装していません。実装する場合、 DVM上のFunctionテーブルのis_implementedをfalseにすることになるかと思います。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1358] Re:その後のVM
投稿者:
2009/06/14 08:20:38

>この方法だと、コンパイル単位が増えてきたとき、プログラムの端のほうでちょろっと >動的リンクが発生しただけで、それこそprint()みたいにあちこちで使われる関数の >呼び出し箇所すべて(通るところだけですが)に対し再リンクが必要になるような……  必ず突っ込まれるで有ろうと予測した部分が突っ込まれました^^。確かに再コンパ イルが頻繁に行われるようであれば、当然負荷が増えるでしょう。でもその場合、 コンパイル時に再リンクも大きな負荷に思えます。  コンパイル時の負荷はまず棚においておいて、実際問題、再コンパイルが頻繁に 行わなければならない処理が多いと思われますか?、実際的に考えればそんな設計は 良い設計とは思えません。  ではなぜ、動的コンパイルが必要だったか。それは、複数の違った処理を一元的 にメニュー管理する目的があったのです。利用目的ですね。このような利用目的が 無い場合、動的コンパイラは不要です。だからdiksamとは指向性が多少違います。  次にスピードの点ですが、現在はVMメイン管理で一元的にカウンターを持ってい ますが、これを関数単位でもてば、(ぱ)さんの指摘する問題は回避できます。  ただ、初期は出来る限り単純なもので済ませたいとの思いがあり。簡単な機構で すめばそのままにしています。  うう~~上記文は多々説明不足な気がすごくしますが、説明を始めると長くなっ てしまいそうなので短文にしています。「もっと説明しろ!」との指摘がありまし たら、ぜひ言ってくださいませ。
[この投稿を含むスレッドを表示] [この投稿を削除]
[1359] Re:その後のVM
投稿者:(ぱ)こと管理人
2009/06/16 01:14:21

> コンパイル時の負荷はまず棚においておいて、実際問題、再コンパイルが頻繁に >行わなければならない処理が多いと思われますか?、実際的に考えればそんな設計は >良い設計とは思えません。 うーん、再コンパイルの頻度は低くても、それが起きるたびに以後の関数呼び出しに ついてすべて一度ずつリンクが入るのはちょっと心配ですがそれはさておき、 ここで埋め込まれる関数インデックスって、VM内で一意になるんですよね? だとすれば、コンパイル単位の破棄の際に破棄対象の関数が使っていたインデックスの ところをぎゅっと圧縮しようとさえ思わなければ、そもそも振りなおし自体不要なのでは ないかと思うのですが……
[この投稿を含むスレッドを表示] [この投稿を削除]
[1362] Re:その後のVM
投稿者:
2009/06/17 19:52:00

>ここで埋め込まれる関数インデックスって、VM内で一意になるんですよね? >だとすれば、コンパイル単位の破棄の際に破棄対象の関数が使っていたインデックスの >ところをぎゅっと圧縮しようとさえ思わなければ、そもそも振りなおし自体不要なのでは >ないかと思うのですが…… 同じ所に、こう書いてあります。 >> 次にスピードの点ですが、現在はVMメイン管理で一元的にカウンターを持ってい >>ますが、これを関数単位でもてば、(ぱ)さんの指摘する問題は回避できます。 >> ただ、初期は出来る限り単純なもので済ませたいとの思いがあり。簡単な機構で >>すめばそのままにしています。 上記のように、今のところ優先順位が低いので、後回しにしています。 もちろん、圧縮等はしていないので、比較的簡単に実装出来ると思います。 たぶん二人とも同じことを考えてると思います。
[この投稿を含むスレッドを表示] [この投稿を削除]