text0006
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
* 川合のプログラミング言語自作のためのテキスト#0006
-(by [[K]], 2019.02.25)
** (11) TJ-03
-お待たせしました。さあついにJITコンパイラです。プログラ...
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h> // 他のOSではここを書き換える.
void loadText(int argc, const char **argv, unsigned char...
int var[256]; // 変数.
unsigned char txt[10000]; // ソースコード.
int pc = 0; // プログラム・カウンター.
unsigned char *code; // 機械語出力先.
int qc = 0; // 出力用のプログラム・カウンター.
void put32(int i)
{
code[qc++] = i & 0xff;
code[qc++] = (i >> 8) & 0xff;
code[qc++] = (i >> 16) & 0xff;
code[qc++] = (i >> 24) & 0xff;
}
#define EAX 0
#define ECX 1
void getNumber(int reg) // 定数は2桁以上でもOK. レジス...
{
unsigned char c = txt[pc], *p, *q;
if ('0' <= c && c <= '9') { // 数字.
p = &txt[pc];
code[qc++] = 0xb8 + reg; // MOV reg,const.
put32(strtol(p, (char **) &q, 0));
pc += q - p;
return;
}
pc++; // 1文字の変数.
code[qc++] = 0x8b; // MOV reg,[const].
code[qc++] = reg * 8 + 0x05;
put32((int) &var[c]);
}
void sub_print(int i)
{
printf("%d\n", i);
}
void sub_time()
{
printf("time=%.3f[sec]\n", clock() / (double) CLOCKS...
}
int main(int argc, const char **argv)
{
int i, j, k, pc0, wqc = 0, wqc2 = 0;
void (*func)(); // funcは関数へのポインタ型の変数.
loadText(argc, argv, txt, 10000);
// Windowsに対して、実行権限のあるメモリを特別に要求.
code = VirtualAlloc(0, 1024 * 1024, MEM_COMMIT, PAGE...
code[qc++] = 0x83; // SUB ESP,124.
code[qc++] = 0xec; // この命令でスタックを調整させる.
code[qc++] = 0x7c;
for (;;) {
pc0 = pc;
if (txt[pc] == 0) // ファイル終端.
break;
if (txt[pc] == '\n' || txt[pc] == ' ' || txt[pc]...
pc++;
} else if (txt[pc + 1] == '=') { // 2文字目が"=".
i = txt[pc]; // 1文字の変数名.
pc += 2;
getNumber(EAX);
if (txt[pc] == ';') {
} else if (txt[pc] == '+') { // 加算.
pc++;
getNumber(ECX);
code[qc++] = 0x01; // ADD EAX,ECX.
code[qc++] = 0xc8;
} else if (txt[pc] == '-') { // 減算.
pc++;
getNumber(ECX);
code[qc++] = 0x29; // SUB EAX,ECX.
code[qc++] = 0xc8;
} else
goto err;
code[qc++] = 0xa3; // MOV [const],EAX.
put32((int) &var[i]);
} else if (txt[pc] == 'p' && txt[pc + 1] == 'r' ...
pc += 6;
getNumber(EAX);
code[qc++] = 0x89; // MOV [ESP],EAX.
code[qc++] = 0x04;
code[qc++] = 0x24;
k = (unsigned char *) sub_print - &code[qc +...
code[qc++] = 0xe8; // CALL sub_print.
put32(k);
} else if (txt[pc] == 'w' && txt[pc + 1] == 'h' ...
wqc = qc;
pc += 6;
getNumber(EAX); // 1文字の変数名.
pc++;
getNumber(ECX);
if (txt[pc] != '{')
goto err;
pc++;
code[qc++] = 0x39; // CMP EAX,ECX.
code[qc++] = 0xc8;
code[qc++] = 0x0f; // JGE ????
code[qc++] = 0x8d;
wqc2 = qc;
put32(0); // ここは後で更新する.
} else if (txt[pc] == '}') {
pc++;
code[qc++] = 0xe9;
put32(wqc - (qc + 4));
k = qc - (wqc2 + 4);
code[wqc2 + 0] = k & 0xff;
code[wqc2 + 1] = (k >> 8) & 0xff;
code[wqc2 + 2] = (k >> 16) & 0xff;
code[wqc2 + 3] = (k >> 24) & 0xff;
} else if (txt[pc] == 't' && txt[pc + 1] == 'i')...
pc += 4;
k = (unsigned char *) sub_time - &code[qc + ...
code[qc++] = 0xe8; // CALL sub_time.
put32(k);
} else
goto err;
}
code[qc++] = 0x83; // ADD ESP,124.
code[qc++] = 0xc4; // 最初に調整したスタックを元に...
code[qc++] = 0x7c;
code[qc++] = 0xc3; // RET.
// for (i = 0; i < qc; i++) { printf("%02x ", code[i]);...
func = (void *) code;
func();
exit(0);
err:
printf("syntax error : %.10s\n", &txt[pc0]);
exit(1);
}
-重要な注意点:
--このプログラムはWindows用に書かれています。しかも32ビッ...
--もしWindows以外で実行したい場合は、以下のように書き換え...
-[Q] macOSではどうやればTJ-03が動くの?
--まず冒頭の #include <windows.h> を以下と入れ替えます。
#include <mach/mach.h>
--そんでもって、codeへの代入文を以下のように書き換えます。
code = malloc(1024 * 1024); vm_protect(mach_task_self(),...
--これで行けるはずです。
--要するにメモリ領域code[]に実行可能権限を付ければいいの...
--そして32ビットアプリとしてコンパイルするのを忘れずに。
-[Q] LinuxではどうやればTJ-03が動くの?
--[[http://mimumimu.net/blog/2011/11/17/Cコード中にマシン...
--この記事をよく読んで、上記のmacOSのやり方も参考にして、...
-このTJ-03はTJ-02と完全互換です。違うのは実行速度だけです...
i=0;
while i<100000000{
i=i+1;
}
print i;
time;
-実行速度を比較してみてください。TJ-02はTL-3と比べて数倍...
-しかもこのJITコンパイラはTJ-02と比べてコードを複雑にしな...
-正直、プログラムの中身は良くわからないところがたくさんあ...
** (12) TJ-03の説明
-機械語が得意でたまらないという奇特な人を除けば、さすがに...
-まず、上記のサンプルプログラムが、どんな機械語に変換され...
|ソースコード|生成された機械語(16進数)|機械語をアセンブラ...
||83 EC 7C|SUB ESP,124|ESP -= 124;|スタック領域に作業用ス...
|i=0;|B8 00 00 00 00|MOV EAX,0|EAX = 0;|これはgetNumber(E...
||A3 xx xx xx xx|MOV [xxxx],EAX|[xxxx] = EAX;|xxxxはvar['...
|while i<100000000{|8B 05 xx xx xx xx|MOV EAX,[xxxx]|EAX ...
||B9 00 E1 F5 05|MOV ECX,100000000|ECX = 100000000;|これ...
||39 C8|CMP EAX,ECX|if (EAX >= ECX)||
||0F 8D 17 00 00 00|JGE $+23|goto 23バイト先|このとび先は...
|i=i+1;|8B 05 xx xx xx xx|MOV EAX,[xxxx]|EAX = [xxxx];|xx...
||B9 01 00 00 00|MOV ECX,1|ECX = 1;|これはgetNumber(ECX)...
||01 C8|ADD EAX,ECX|EAX += ECX;||
||A3 xx xx xx xx|MOV [xxxx],EAX|[xxxx] = EAX;|xxxxはvar['...
|}|E9 D6 FF FF FF|JMP $-42|goto 42バイト前|これはwhile命...
|print i;|8B 05 xx xx xx xx|MOV EAX,[xxxx]|EAX = [xxxx];|...
||89 04 24|MOV [ESP],EAX|[ESP] = EAX;|第一引数にセット(...
||E8 xx xx xx xx|CALL _sub_print|sub_print();|C言語で書か...
|time;|E8 xx xx xx xx|CALL _sub_time|sub_time();|C言語で...
||83 C4 7C|ADD ESP,124|ESP += 124;|スタック領域の作業用ス...
||C3|RET|return;|機械語関数を終了(ソースコードに無関係に...
-x86の32ビットの機械語では、レジスタと呼ばれるCPU内の変数...
--またこのESPはスタックの管理のために使うレジスタなので、...
-TJ-03では変数iなどはvar[]の中にあり、つまりこれはメモリ...
-機械語は覚えるのが容易ではないので、コード表などから調べ...
--簡易版のコード表: [[text0006a]]
** (13) なぜ32ビットコードにしたのか?
-今は64ビット対応のCPUがかなり普及して、OSやコンパイラも6...
-64ビット化することで大きく変わるのは、使えるレジスタの数...
-仮に割り付けを十分に頑張ったとして、レジスタが倍増すると...
-また64ビットの機械語はややこしいことになっているので、32...
-もちろん32ビットのJITコンパイラが書けるようになってから...
** 次回に続く
-次回: [[text0007]]
*こめんと欄
-セキュリティキャンプに挑戦中のものです。このlinuxで実行...
-無事問題が解決し、このJITコンパイラを試すことができまし...
-自己解決素晴らしいです! -- [[K]] SIZE(10){2019-05-05 (...
#comment
終了行:
* 川合のプログラミング言語自作のためのテキスト#0006
-(by [[K]], 2019.02.25)
** (11) TJ-03
-お待たせしました。さあついにJITコンパイラです。プログラ...
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <windows.h> // 他のOSではここを書き換える.
void loadText(int argc, const char **argv, unsigned char...
int var[256]; // 変数.
unsigned char txt[10000]; // ソースコード.
int pc = 0; // プログラム・カウンター.
unsigned char *code; // 機械語出力先.
int qc = 0; // 出力用のプログラム・カウンター.
void put32(int i)
{
code[qc++] = i & 0xff;
code[qc++] = (i >> 8) & 0xff;
code[qc++] = (i >> 16) & 0xff;
code[qc++] = (i >> 24) & 0xff;
}
#define EAX 0
#define ECX 1
void getNumber(int reg) // 定数は2桁以上でもOK. レジス...
{
unsigned char c = txt[pc], *p, *q;
if ('0' <= c && c <= '9') { // 数字.
p = &txt[pc];
code[qc++] = 0xb8 + reg; // MOV reg,const.
put32(strtol(p, (char **) &q, 0));
pc += q - p;
return;
}
pc++; // 1文字の変数.
code[qc++] = 0x8b; // MOV reg,[const].
code[qc++] = reg * 8 + 0x05;
put32((int) &var[c]);
}
void sub_print(int i)
{
printf("%d\n", i);
}
void sub_time()
{
printf("time=%.3f[sec]\n", clock() / (double) CLOCKS...
}
int main(int argc, const char **argv)
{
int i, j, k, pc0, wqc = 0, wqc2 = 0;
void (*func)(); // funcは関数へのポインタ型の変数.
loadText(argc, argv, txt, 10000);
// Windowsに対して、実行権限のあるメモリを特別に要求.
code = VirtualAlloc(0, 1024 * 1024, MEM_COMMIT, PAGE...
code[qc++] = 0x83; // SUB ESP,124.
code[qc++] = 0xec; // この命令でスタックを調整させる.
code[qc++] = 0x7c;
for (;;) {
pc0 = pc;
if (txt[pc] == 0) // ファイル終端.
break;
if (txt[pc] == '\n' || txt[pc] == ' ' || txt[pc]...
pc++;
} else if (txt[pc + 1] == '=') { // 2文字目が"=".
i = txt[pc]; // 1文字の変数名.
pc += 2;
getNumber(EAX);
if (txt[pc] == ';') {
} else if (txt[pc] == '+') { // 加算.
pc++;
getNumber(ECX);
code[qc++] = 0x01; // ADD EAX,ECX.
code[qc++] = 0xc8;
} else if (txt[pc] == '-') { // 減算.
pc++;
getNumber(ECX);
code[qc++] = 0x29; // SUB EAX,ECX.
code[qc++] = 0xc8;
} else
goto err;
code[qc++] = 0xa3; // MOV [const],EAX.
put32((int) &var[i]);
} else if (txt[pc] == 'p' && txt[pc + 1] == 'r' ...
pc += 6;
getNumber(EAX);
code[qc++] = 0x89; // MOV [ESP],EAX.
code[qc++] = 0x04;
code[qc++] = 0x24;
k = (unsigned char *) sub_print - &code[qc +...
code[qc++] = 0xe8; // CALL sub_print.
put32(k);
} else if (txt[pc] == 'w' && txt[pc + 1] == 'h' ...
wqc = qc;
pc += 6;
getNumber(EAX); // 1文字の変数名.
pc++;
getNumber(ECX);
if (txt[pc] != '{')
goto err;
pc++;
code[qc++] = 0x39; // CMP EAX,ECX.
code[qc++] = 0xc8;
code[qc++] = 0x0f; // JGE ????
code[qc++] = 0x8d;
wqc2 = qc;
put32(0); // ここは後で更新する.
} else if (txt[pc] == '}') {
pc++;
code[qc++] = 0xe9;
put32(wqc - (qc + 4));
k = qc - (wqc2 + 4);
code[wqc2 + 0] = k & 0xff;
code[wqc2 + 1] = (k >> 8) & 0xff;
code[wqc2 + 2] = (k >> 16) & 0xff;
code[wqc2 + 3] = (k >> 24) & 0xff;
} else if (txt[pc] == 't' && txt[pc + 1] == 'i')...
pc += 4;
k = (unsigned char *) sub_time - &code[qc + ...
code[qc++] = 0xe8; // CALL sub_time.
put32(k);
} else
goto err;
}
code[qc++] = 0x83; // ADD ESP,124.
code[qc++] = 0xc4; // 最初に調整したスタックを元に...
code[qc++] = 0x7c;
code[qc++] = 0xc3; // RET.
// for (i = 0; i < qc; i++) { printf("%02x ", code[i]);...
func = (void *) code;
func();
exit(0);
err:
printf("syntax error : %.10s\n", &txt[pc0]);
exit(1);
}
-重要な注意点:
--このプログラムはWindows用に書かれています。しかも32ビッ...
--もしWindows以外で実行したい場合は、以下のように書き換え...
-[Q] macOSではどうやればTJ-03が動くの?
--まず冒頭の #include <windows.h> を以下と入れ替えます。
#include <mach/mach.h>
--そんでもって、codeへの代入文を以下のように書き換えます。
code = malloc(1024 * 1024); vm_protect(mach_task_self(),...
--これで行けるはずです。
--要するにメモリ領域code[]に実行可能権限を付ければいいの...
--そして32ビットアプリとしてコンパイルするのを忘れずに。
-[Q] LinuxではどうやればTJ-03が動くの?
--[[http://mimumimu.net/blog/2011/11/17/Cコード中にマシン...
--この記事をよく読んで、上記のmacOSのやり方も参考にして、...
-このTJ-03はTJ-02と完全互換です。違うのは実行速度だけです...
i=0;
while i<100000000{
i=i+1;
}
print i;
time;
-実行速度を比較してみてください。TJ-02はTL-3と比べて数倍...
-しかもこのJITコンパイラはTJ-02と比べてコードを複雑にしな...
-正直、プログラムの中身は良くわからないところがたくさんあ...
** (12) TJ-03の説明
-機械語が得意でたまらないという奇特な人を除けば、さすがに...
-まず、上記のサンプルプログラムが、どんな機械語に変換され...
|ソースコード|生成された機械語(16進数)|機械語をアセンブラ...
||83 EC 7C|SUB ESP,124|ESP -= 124;|スタック領域に作業用ス...
|i=0;|B8 00 00 00 00|MOV EAX,0|EAX = 0;|これはgetNumber(E...
||A3 xx xx xx xx|MOV [xxxx],EAX|[xxxx] = EAX;|xxxxはvar['...
|while i<100000000{|8B 05 xx xx xx xx|MOV EAX,[xxxx]|EAX ...
||B9 00 E1 F5 05|MOV ECX,100000000|ECX = 100000000;|これ...
||39 C8|CMP EAX,ECX|if (EAX >= ECX)||
||0F 8D 17 00 00 00|JGE $+23|goto 23バイト先|このとび先は...
|i=i+1;|8B 05 xx xx xx xx|MOV EAX,[xxxx]|EAX = [xxxx];|xx...
||B9 01 00 00 00|MOV ECX,1|ECX = 1;|これはgetNumber(ECX)...
||01 C8|ADD EAX,ECX|EAX += ECX;||
||A3 xx xx xx xx|MOV [xxxx],EAX|[xxxx] = EAX;|xxxxはvar['...
|}|E9 D6 FF FF FF|JMP $-42|goto 42バイト前|これはwhile命...
|print i;|8B 05 xx xx xx xx|MOV EAX,[xxxx]|EAX = [xxxx];|...
||89 04 24|MOV [ESP],EAX|[ESP] = EAX;|第一引数にセット(...
||E8 xx xx xx xx|CALL _sub_print|sub_print();|C言語で書か...
|time;|E8 xx xx xx xx|CALL _sub_time|sub_time();|C言語で...
||83 C4 7C|ADD ESP,124|ESP += 124;|スタック領域の作業用ス...
||C3|RET|return;|機械語関数を終了(ソースコードに無関係に...
-x86の32ビットの機械語では、レジスタと呼ばれるCPU内の変数...
--またこのESPはスタックの管理のために使うレジスタなので、...
-TJ-03では変数iなどはvar[]の中にあり、つまりこれはメモリ...
-機械語は覚えるのが容易ではないので、コード表などから調べ...
--簡易版のコード表: [[text0006a]]
** (13) なぜ32ビットコードにしたのか?
-今は64ビット対応のCPUがかなり普及して、OSやコンパイラも6...
-64ビット化することで大きく変わるのは、使えるレジスタの数...
-仮に割り付けを十分に頑張ったとして、レジスタが倍増すると...
-また64ビットの機械語はややこしいことになっているので、32...
-もちろん32ビットのJITコンパイラが書けるようになってから...
** 次回に続く
-次回: [[text0007]]
*こめんと欄
-セキュリティキャンプに挑戦中のものです。このlinuxで実行...
-無事問題が解決し、このJITコンパイラを試すことができまし...
-自己解決素晴らしいです! -- [[K]] SIZE(10){2019-05-05 (...
#comment
ページ名: