0

私は16ビットのレジスタベースの仮想マシンを持っています。実際のx86マシンコードをコンパイルする手順は何ですか?コンパイルされたコードを別の実行可能ファイル/ DLLとリンクできる必要がある場合を除き、私はJITコンパイラを作成することを検討していません。VM固有のコードをx86マシンコードにコンパイルするにはどうすればよいですか?

VMは、VMがプロジェクトに追加された場合、特別な言語構成を追加できるようになっています。 (例えば、それがゲームエンジンに埋め込まれている場合、「エンティティ」オブジェクトタイプが追加され、エンジンのいくつかのC関数が公開される可能性があります)。これにより、公開された特定のC関数にコードが完全に依存する公開されているC++クラスを、埋め込まれているアプリケーションに追加します。

スクリプトコードをVMバイトコードからネイティブEXEにコンパイルすると、このような「リンク」が可能ですか?

これはLuaのVMのようにレジスタベースです。すべての基本変数は巨大なC配列である「レジスタ」に格納されています。スコープが変更されると、レジスタポインタはインクリメントまたはデクリメントされるため、レジスタ番号は相対的であり、スタックポインタと同様です。例えば:

int a = 5; 
{ 
    int a = 1; 
} 

仮想マシンの擬似アセンブリでは、次のようになります。

mov_int (%r0, $5) 

; new scope, the "register pointer" is then incremented by the number 
; of bytes that are used to store local variables in this new scope. E.g. int = 4 bytes 
; say $rp is the "register pointer" 

add  (%rp, $4) ; since size of int is usually 4 bytes 
        ; this is if registers are 1 bytes in size, if they were 
        ; 4 bytes in size it would just be adding $1 

mov_int (%r0, $1) ; now each register "index" is offset by 4, 
        ; this is now technically setting %r4 
        ; different instructions are used to get values above current scope 

sub (%rp, $4) ; end of scope so reset %rp 

この部分についての私の質問は、私はこの種のもののためにスタック・ポインタを使用する必要があります、ありますか?ベースポインタ?このコンセプトを置き換えるのに私は何が使えますか?

+1

あなたの質問はQ&Aサイトでは広すぎます。あなたは本や大学のコースが必要です。 – EJP

+0

私はそれをはるかに広範ではなく、より明確にしたので、2つの簡単な質問に沸きます。 – Accumulator

+0

私はあなたがここで実際に答えることができる質問を持っていると思いますが、あなたが求めていることを正確に理解するのは少し難しいと思っています。私はあなたを助けたいと思っていますが、質問をもう一度試して言い換えることができれば、本当に助けてくれるでしょう。) – Cauterite

答えて

0

VMは、VMがプロジェクトに追加された場合、特別な言語構成を追加できるようになっています。 (例えば、それがゲームエンジンに埋め込まれている場合、「エンティティ」オブジェクトタイプが追加され、エンジンのいくつかのC関数が公開される可能性があります)。これにより、公開された特定のC関数にコードが完全に依存する公開されているC++クラスを、埋め込まれているアプリケーションに追加します。

この種のクロスランゲージインターフェイスを実装する方法はたくさんあります。 VMバイトコードまたはネイティブマシンコードを実行しているかどうかは、非常に低いオーバーヘッドのインターフェイスが必要な場合を除き、ここではあまり重要ではありません。主な考慮事項は、言語の性質、特に静的または動的な型定義があるかどうかです。

  • (a)の言語「外国機能インターフェイス」アプローチ、:

    は、一般的に、これらの2つの最も一般的なアプローチ(あなたはすでに彼らに精通しているかもしれない)です話します/ runtimeは、Cからの関数とデータを自動的にラップする機能を提供します。例には、LuaJIT FFI,js-ctypesおよびP/Invokeが含まれます。ほとんどのFFIはCDECL/STDCALL関数とPOD構造で動作できます。いくつかはC++またはCOMクラスのさまざまなサポートレベルを持っています。あなたのランタイムが手動で構築/言語で使用するためのオブジェクトを操作するために使用できるCのAPIを公開し

  • (b)はランタイムAPI」アプローチ、。 Luaには広範なAPIがあります(example)。Pythonもあります。スクリプトコードをネイティブEXEにVM​​のバイトコードからコンパイルされている場合

はどのように「リンク」のこの種のは可能でしょうか?

あなたはおそらく、あなたの生成した機械コードに外部機能アドレスを焼き付けます。もしあなたが適切なFFIインフラストラクチャを持っていれば、共有ライブラリのインポートがどのように機能するか(インポートアドレステーブル、再配置、フィックスアップなど)を知っている限り、これを行うことはできません。

共有ライブラリについてよくわからない場合は、その領域を調査することで、FFIをコンパイラに実装する方法をはっきりと理解することができます。

たとえば、LoadLibrary(),GetProcAddress()のようなやや動的なアプローチを行う方が簡単な場合は、関数ポインタを言語のオブジェクトとしてラップします。

問題の言語/ VMについて何も知らなくても、より具体的な提案をすることは残念ながら非常に難しいです。


[...]この部分についての私の質問があり、私はこの種のもののためにスタック・ポインタを使用する必要がありますか?ベースポインタ?このコンセプトを置き換えるのに私は何が使えますか?

この「レジスタ配列」スキームの目的が完全にわかりません。

語彙スコープのある言語では、関数をコンパイルするときに、通常、本体に宣言されているすべての変数を列挙し、すべての変数を保持するのに十分な大きさのスタック空間を割り当てます(CPUレジスタの複雑なトピック割り当て)。コードは、スタックポインタまたは(より頻繁に)ベースポインタを使用して、これらの変数に対処することができます。

例のように外側のスコープ内の変数に影を付けると、スタック上に別々のメモリスペースが割り当てられます。これは、コンパイラに関しては異なる変数ですからです。

VMが使用しているスキームの背後にある論理的根拠を理解していないと、機械コードにどのように変換すべきかを実際に示唆することはできません。たぶん、バイトコードコンパイラのプログラミング経験が豊富な人が、あなたの答えをあなたに与えることができます。

しかし、あなたのVMのアプローチは、私が記述したものと実際に似ているかもしれません。この場合、機械コードのコンパイルに適合させるのは、実際には非常に簡単です - 仮想ローカル変数メモリ空間をスタックスペース。

1

あなたの質問が正しく理解されたら、はい、ここでSP/BPなどを使用する必要があります。それがネイティブマシンコードにコンパイルすることは、プログラムの上位レベルの動作を、実行中のオペレーティングシステムの規則に従った同等のマシン命令に変換することを意味します。

したがって、アセンブラから呼び出す場合は、ホスト提供の関数を呼び出すのと同じことをする必要があります。これは通常、関数引数の値を適切なレジスタに張り付け、スタックにプッシュし、必要に応じて変換し、CALLまたはJMP命令を生成するか、CPUが実際にその関数のメモリアドレスにジャンプすることを期待するものであることを意味します。

ホストがあなたに提供するポインタマッピングを機能させるためには、関数名のテーブルを用意し、そこからアドレスを調べる必要があります。

関数が戻ったら、必要に応じて返された関数の値を内部の型に変換して、うまくいくようにします。 (これは基本的に、これらの「外部関数インタフェース」ライブラリが内部的に行うことです)。

言語や使用方法によっては、ここで不正行為をする可能性もあります。独自の内部擬似スタックを使用して、特別な「ネイティブ関数を呼び出す」命令を追加することができます。この命令は、その関数に関する情報をパラメータとして受け取る(例えば、それが受け取る/返すパラメータ型、関数ポインタを調べる方法)。そして、外部関数インタフェースライブラリを使用して実際の関数呼び出しを行う。

これは、ネイティブ関数を呼び出すにはわずかなオーバーヘッドがありますが、あなたのVMをそのまま保つことができ、ネイティブコードを呼び出してアプリケーションと統合できることを意味します。

関連する問題