可能です。動的コード生成は、ソフトウェア レンダリングやグラフィックスなどの一部の分野では主流です。あらゆる種類のスクリプト言語、マシン コード (私の知る限り、.NET、Java、Perl。最近では JavaScript もクラブに加わりました) でのバイトコードの動的コンパイルで、多くの用途があります。
また、非常に数学の多いアプリケーションでも使用されていることがわかります。たとえば、そのような乗算を数千回実行する予定がある場合、行列の乗算からゼロを使用してすべての乗算を削除すると、違いが生じます。
コードの SSA 表現を読むことを強くお勧めします。これは、各プリミティブがいわゆる 3 オペランド形式に変換され、各変数が 1 回だけ割り当てられる表現です (したがって、同じ静的単一割り当て形式)。
そのようなコードに対して高次の最適化を実行でき、そのコードを実行可能なコードに変換するのは簡単です。ただし、週末にそのコード生成バックエンドを作成することはありません...
SSA がどのように見えるかを理解するには、LLVM コンパイラを試してみてください。彼らの Web サイトには、ちょっとした "Try Out" ウィジェットがあります。C コードをウィンドウに貼り付けると、SSA フォームに近いものが得られます。
それがどのように見えるかの小さな例:
この整数平方根アルゴリズムを C で見てみましょう (任意の例です。単純でありながら自明ではないものを取り上げました)。
unsigned int isqrt32 (unsigned int value)
{
unsigned int g = 0;
unsigned int bshift = 15;
unsigned int b = 1<<bshift;
do {
unsigned int temp = (g+g+b)<<bshift;
if (value >= temp) {
g += b;
value -= temp;
}
b>>=1;
} while (bshift--);
return g;
}
LLVM はそれを次のように変換します。
define i32 @isqrt32(i32 %value) nounwind {
entry:
br label %bb
bb: ; preds = %bb, %entry
%indvar = phi i32 [ 0, %entry ], [ %indvar.next, %bb ]
%b.0 = phi i32 [ 32768, %entry ], [ %tmp23, %bb ]
%g.1 = phi i32 [ 0, %entry ], [ %g.0, %bb ]
%value_addr.1 = phi i32 [ %value, %entry ], [ %value_addr.0, %bb ]
%bshift.0 = sub i32 15, %indvar
%tmp5 = shl i32 %g.1, 1
%tmp7 = add i32 %tmp5, %b.0
%tmp9 = shl i32 %tmp7, %bshift.0
%tmp12 = icmp ult i32 %value_addr.1, %tmp9
%tmp17 = select i1 %tmp12, i32 0, i32 %b.0
%g.0 = add i32 %tmp17, %g.1
%tmp20 = select i1 %tmp12, i32 0, i32 %tmp9
%value_addr.0 = sub i32 %value_addr.1, %tmp20
%tmp23 = lshr i32 %b.0, 1
%indvar.next = add i32 %indvar, 1
%exitcond = icmp eq i32 %indvar.next, 16
br i1 %exitcond, label %bb30, label %bb
bb30: ; preds = %bb
ret i32 %g.0
}
私はそれが最初は恐ろしく見えることを知っています。純粋な SSA-Form でさえありません。その表現を読めば読むほど、それがより理にかなっているでしょう。また、この表現が最近広く使用されている理由もわかります。
必要なすべての情報をデータ構造にカプセル化するのは簡単です。最後に、オペコード名などに列挙型または文字列を使用するかどうかを決定する必要があります。
ところで、私はあなたにデータ構造を提供したのではなく、より形式的で実用的な言語と、どこを見ればよいかのアドバイスを提供したことを知っています.
とても素晴らしく興味深い研究分野です。
編集: 忘れる前に: .NET と Java の組み込み機能を見落とさないでください。これらの言語を使用すると、プログラム内のバイト コードまたはソース コードからコンパイルし、結果を実行できます。
乾杯、ニルス
あなたの編集について:コードでバイナリブロブを実行する方法:
バイナリ BLOB へのジャンプは、OS とプラットフォームに依存します。一言で言えば、命令キャッシュを無効にしました。おそらくデータキャッシュを書き戻す必要があり、コードを書き込んだメモリ領域で実行権を有効にする必要があるかもしれません。
win32 では、コードをヒープに配置すれば命令キャッシュのフラッシュで十分と思われるため、比較的簡単です。
このスタブを使用して開始できます。
typedef void (* voidfunc) (void);
void * generate_code (void)
{
// reserve some space
unsigned char * buffer = (unsigned char *) malloc (1024);
// write a single RET-instruction
buffer[0] = 0xc3;
return buffer;
}
int main (int argc, char **args)
{
// generate some code:
voidfunc func = (voidfunc) generate_code();
// flush instruction cache:
FlushInstructionCache(GetCurrentProcess(), func, 1024);
// execute the code (it does nothing atm)
func();
// free memory and exit.
free (func);
}