6

単純なVMを実装しており、現在、ランタイム演算を使用して、ベースポインターからのオフセットとして個々のプログラムオブジェクトアドレスを計算しています。

今日はこの件についていくつか質問をしましたが、どこにもゆっくりと進んでいないようです。

質問1からいくつかのことを学びました- オブジェクトと構造体のメンバーアクセスとアドレスオフセットの計算-最新のプロセッサには仮想アドレス指定機能があり、算術演算に専念する追加のサイクルなしでメモリオフセットを計算できることを学びました。

そして質問2から-アドレスオフセットはC/C ++のコンパイル時に解決されますか?-オフセットを手動で行う場合、これが発生する保証はないことを学びました。

ここまでで、私が達成したいのは、ハードウェアの仮想メモリアドレス指定機能を利用し、それらをランタイムからオフロードすることであることは明らかです。

プラットフォームに関してはGCCを使用しています-Windowsのx86で開発していますが、VMであるため、GCCでサポートされているすべてのプラットフォームで効率的に実行したいと考えています。

したがって、この主題に関する情報は歓迎され、非常に高く評価されます。

前もって感謝します!

編集:私のプログラムコード生成の概要-設計段階では、プログラムはツリー階層として構築され、オブジェクトにインデックスを付け、プログラムメモリブロックの先頭からのオフセットを計算するとともに、1つの連続したメモリブロックに再帰的にシリアル化されます。

編集2:VMの擬似コードは次のとおりです。

switch *instruction
   case 1: call_fn1(*(instruction+1)); instruction += (1+sizeof(parameter1)); break;
   case 2: call_fn2(*(instruction+1), *(instruction+1+sizeof(parameter1));
           instruction += (1+sizeof(parameter1)+sizeof(parameter2); break;
   case 3: instruction += *(instruction+1); break;  

ケース1は、命令の直後にある1つのパラメーターを受け取る関数であるため、命令から1バイトのオフセットとして渡されます。命令ポインタは、次の命令を見つけるために1+最初のパラメータのサイズだけインクリメントされます。

ケース2は、前と同じように2つのパラメーターを受け取る関数で、最初のパラメーターは1バイトのオフセットとして渡され、2番目のパラメーターは1バイトのオフセットと最初のパラメーターのサイズとして渡されます。次に、命令ポインタは、命令のサイズと両方のパラメータのサイズによってインクリメントされます。

ケース3はgotoステートメントであり、命令ポインターはgoto命令の直後に続くオフセットによってインクリメントされます。

編集3:私の理解では、OSは各プロセスに専用の仮想メモリアドレス空間を提供します。もしそうなら、これは最初のアドレスが常に...よくゼロであることを意味します、それでメモリブロックの最初のバイトからのオフセットは実際にはこの要素のまさにアドレスです?メモリアドレスがすべてのプロセス専用であり、プログラムメモリブロックのオフセットとメモリブロックの最初のバイトからのすべてのプログラムオブジェクトのオフセットがわかっている場合、オブジェクトアドレスはコンパイル時に解決されますか?

問題は、これらのオフセットがCコードのコンパイル中に使用できないことです。これらのオフセットは、「コンパイル」フェーズおよびバイトコードへの変換中に認識されるようになります。これは、「無料」のオブジェクトメモリアドレス計算を行う方法がないことを意味しますか?

たとえば、仮想マシンのみがマシンコードにコンパイルされるJavaでこれはどのように行われるのでしょうか。これは、実行時の演算のために、オブジェクトアドレスの計算にパフォーマンスの低下が生じることを意味しますか?

4

4 に答える 4

2

リンクされた質問と回答がこの状況にどのように適用されるかを明らかにする試みがあります。

最初の質問に対する答えは、2つの異なるものを組み合わせたものです。1つはX86命令のアドレッシングモードであり、2つ目は仮想アドレスから物理アドレスへのマッピングです。1つ目はコンパイラーによって実行されるもので、2つ目は(通常)オペレーティングシステムによってセットアップされるものです。あなたの場合、あなたは最初のものだけを心配するべきです。

X86アセンブリの命令には、メモリアドレスへのアクセス方法に大きな柔軟性があります。メモリの読み取りまたは書き込みを行う命令のアドレスは、次の式に従って計算されます。

segment + base + index * size + offset

アドレスのセグメント部分はほとんどの場合デフォルトのDSセグメントであり、通常は無視できます。このbase部分は、汎用レジスターの1つまたはスタックポインターによって与えられます。パーツは汎用レジスタのindex1つによって指定され、サイズは1、2、4、または8のいずれかです。最後に、オフセットは命令に埋め込まれた定数値です。これらの各コンポーネントはオプションですが、明らかに少なくとも1つ指定する必要があります。

このアドレス指定機能は、明示的な算術命令なしでアドレスを計算することについて話すときに一般的に意味されるものです。コメント提供者の1人が言及した特別な命令があります。LEAこれはアドレス計算を行いますが、メモリの読み取りまたは書き込みの代わりに、計算されたアドレスをレジスタに格納します。

質問に含めたコードの場合、コンパイラがこれらのアドレッシングモードを使用して明示的な算術命令を回避することは非常に妥当です。

例として、instruction変数の現在の値をレジスタに保持できますESI。さらに、とのそれぞれsizeof(parameter1)sizeof(parameter2)コンパイル時定数です。標準のX86呼び出し規約では、関数の引数は逆の順序でプッシュされるため(したがって、最初の引数はスタックの一番上にあります)、アセンブリコードは次のようになります。

case1: 
  PUSH [ESI+1]
  CALL fn1
  ADD ESP,4 ; drop arguments from stack
  ADD ESI,5
  JMP end_switch
case2: 
  PUSH [ESI+5]
  PUSH [ESI+1]
  CALL fn2
  ADD ESP,8 ; drop arguments from stack
  ADD ESI,9
  JMP end_swtich
case3:
  MOV ESI,[ESI+1]
  JMP end_switch
end_switch:

これは、両方のパラメーターのサイズが4バイトであることを前提としています。もちろん、実際のコードはコンパイラー次第であり、ある程度の最適化を要求する限り、コンパイラーがかなり効率的なコードを出力することを期待するのは合理的です。

于 2012-07-15T20:49:23.853 に答える
1

XVMの相対アドレスにデータ項目がありA、(たとえば)という命令がありますpush Xか?Aまた、VMのデータ領域のベースアドレスに追加しなくても、この命令を実行できるようにする必要があります。

VMのデータ領域を固定仮想アドレスにマッピングすることでこの問題を解決するVMを作成しました。コンパイラはこの仮想アドレスを認識しているためA、コンパイル時に調整できます。このソリューションはあなたのために働きますか?コンパイラを自分で変更できますか?

私のVMはスマートカード上で実行され、OSを完全に制御できるため、環境はあなたの環境とは大きく異なります。ただし、Windowsには、固定アドレスにメモリを割り当てるための機能がいくつかあります。たとえば、 VirtualAlloc関数です。これを試してみてください。試してみると、Windowsが固定アドレスのデータ領域と衝突する領域を割り当てていることに気付くかもしれません。そのため、VMのデータ領域を割り当てた後、使用するDLLを手動でロードする必要があります。

しかし、おそらく予期せぬ問題を克服する必要があり、問題を起こす価値はないかもしれません。

于 2012-07-15T19:04:51.653 に答える
0

仮想アドレス変換、ページテーブル、またはTLBで遊ぶことは、OSカーネルレベルでのみ実行できるものであり、プラットフォームとプロセッサフ​​ァミリ間で移植することはできません。さらに、ほとんどのCPU ISAでのハードウェアアドレス変換は、通常、特定のページサイズのレベルでのみサポートされます。

于 2012-07-15T18:33:05.223 に答える
0

私が得た多くの回答に基づいて、私自身の質問に答えるために。

私の状況では、達成したいことは実際には不可能であることがわかりました。メモリアドレスの計算を無料で取得できるのは、特定の要件が満たされ、特定の命令をマシンにコンパイルする必要がある場合のみです。

私は、教育目的で視覚要素、レゴスタイルのドラッグアンドドロッププログラミング環境を開発しています。これは、プログラムコードを実行するために単純なVMに依存しています。パフォーマンスを最大化することを望んでいましたが、私のシナリオではそれは不可能です。プログラム要素は同等のCコードを生成することもできるため、それほど大きな問題ではありません。Cコードは、パフォーマンスを最大化するために従来どおりにコンパイルできます。

よくわからないことを答えてくれた皆さん、ありがとうございました!

于 2012-07-15T20:07:12.517 に答える