text0014
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
* 川合のプログラミング言語自作のためのテキスト#0005
-(by [[K]], 2019.06.29)
** (10) TJ-03a
-お待たせしました。さあついにJITコンパイラです。プログラ...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <windows.h>
typedef unsigned char *String; // こう書くと String は u...
void loadText(int argc, const char **argv, unsigned char...
int isAlphabet(unsigned char c) → TL-2cと同じなので省略
int lexer(String s, String b, String t[]) → TL-2cと同じ...
enum { EAX = 0, ECX = 1 }; // 定数宣言.
void sub_print(int i)
{
printf("%d\n", i);
}
void sub_time()
{
printf("time=%.3f[sec]\n", clock() / (double) CLOCKS...
}
int put32(unsigned char *q, int i)
{
q[0] = i & 0xff;
q[1] = (i >> 8) & 0xff;
q[2] = (i >> 16) & 0xff;
q[3] = (i >> 24) & 0xff;
return 4;
}
int main(int argc, const char **argv)
{
static String def[] = { ";", "=", "+", "-", "print",...
int i, vars = 0, pc, pc1, qc = 0, wqc = 0, wqc2 = 0,...
String t[1000], varName[256]; // トークンと変数名.
unsigned char txt[10000], buf[10000]; // ソースコー...
unsigned char *code = VirtualAlloc(0, 1024 * 1024, M...
void (*func)(); // funcは関数へのポインタ型の変数.
loadText(argc, argv, txt, 10000);
pc1 = lexer(txt, buf, t);
t[pc1] = t[pc1 + 1] = t[pc1 + 2] = t[pc1 + 3] = ""; ...
for (i = 0; def[i] != 0; i++)
varName[i] = def[i];
vars = i;
code[qc++] = 0x83; // SUB ESP,124. この命令でスタッ...
code[qc++] = 0xec;
code[qc++] = 0x7c;
for (pc = 0; pc < pc1; pc++) {
for (i = 0; i < vars; i++) { // 登録済みの中から...
if (strcmp(t[pc], varName[i]) == 0)
break;
}
if (i == vars) {
varName[i] = t[pc]; // 見つからなかったので...
var[i] = strtol(t[pc], 0, 0); // 初期値を設定.
vars++;
}
varNum[pc] = i;
}
for (pc = 0; pc < pc1; pc++) {
if (varNum[pc + 1] == 1 /* = */) { // 2単語目が"...
code[qc++] = 0x8b; // MOV EAX,[...]
code[qc++] = EAX * 8 + 0x05;
qc += put32(&code[qc], (int) &var[varNum[pc ...
if (varNum[pc + 3] == 0 /* ; */) { // 単純代...
} else if (varNum[pc + 3] == 2 /* + */ && va...
code[qc++] = 0x8b; // MOV ECX,[...]
code[qc++] = ECX * 8 + 0x05;
qc += put32(&code[qc], (int) &var[varNum...
code[qc++] = 0x01;
code[qc++] = 0xc8;
} else if (varNum[pc + 3] == 3 /* - */ && va...
code[qc++] = 0x8b; // MOV ECX,[...]
code[qc++] = ECX * 8 + 0x05;
qc += put32(&code[qc], (int) &var[varNum...
code[qc++] = 0x29;
code[qc++] = 0xc8;
} else
goto err;
code[qc++] = 0xa3; // MOV [...],EAX
qc += put32(&code[qc], (int) &var[varNum[pc]...
} else if (varNum[pc] == 5 /* while */ && varNum...
wqc = qc;
code[qc++] = 0x8b; // MOV EAX,[...]
code[qc++] = EAX * 8 + 0x05;
qc += put32(&code[qc], (int) &var[varNum[pc ...
code[qc++] = 0x8b; // MOV ECX,[...]
code[qc++] = ECX * 8 + 0x05;
qc += put32(&code[qc], (int) &var[varNum[pc ...
code[qc++] = 0x39;
code[qc++] = 0xc8;
code[qc++] = 0x0f;
code[qc++] = 0x8d;
qc += put32(&code[qc], 0);
wqc2 = qc;
pc += 7 - 1;
continue;
} else if (varNum[pc] == 10 /* } */) {
code[qc++] = 0xe9;
qc += put32(&code[qc], &code[wqc] - &code[qc...
put32(&code[wqc2 - 4], &code[qc] - &code[wqc...
continue;
} else if (varNum[pc] == 11 /* time */ && varNum...
code[qc++] = 0xe8;
qc += put32(&code[qc], (unsigned char *) sub...
} else if (varNum[pc] == 4 /* print */ && varNum...
code[qc++] = 0x8b; // MOV EAX,[...]
code[qc++] = EAX * 8 + 0x05;
qc += put32(&code[qc], (int) &var[varNum[pc ...
code[qc++] = 0x89;
code[qc++] = 0x04;
code[qc++] = 0x24;
code[qc++] = 0xe8;
qc += put32(&code[qc], (unsigned char *) sub...
} else
goto err;
while (varNum[pc] != 0 /* ; */)
pc++;
}
code[qc++] = 0x83; // ADD ESP,124. 最初に調整したス...
code[qc++] = 0xc4;
code[qc++] = 0x7c;
code[qc++] = 0xc3; // RET
func = (void *) code;
func();
exit(0);
err:
printf("syntax error : %s %s %s %s\n", t[pc], t[pc +...
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-03aはTL-3cやTL-3dと完全互換です。違うのは実行速度...
i = 0;
while (i < 100000000) {
i = i + 1;
}
print i;
time;
-実行速度を比較してみてください。TL-3dはTL-3cと比べて11.3...
-しかもこのJITコンパイラはTL-3dと比べてコードを複雑にしな...
-正直、プログラムの中身は良くわからないところがたくさんあ...
** (11) TJ-03aの説明
-機械語が得意でたまらないという奇特な人を除けば、さすがに...
-まず、上記のサンプルプログラムが、どんな機械語に変換され...
|ソースコード|生成された機械語(16進数)|機械語をアセンブラ...
||83 EC 7C|SUB ESP,124|ESP -= 124;|スタック領域に作業用ス...
|i = 0;|8B 05 xx xx xx xx|MOV EAX,[xxxxx]|EAX = [xxxx];|x...
||A3 xx xx xx xx|MOV [xxxx],EAX|[xxxx] = EAX;|xxxxは変数...
|while (i < 100000000) {|8B 05 xx xx xx xx|MOV EAX,[xxxx]...
||8B 0D xx xx xx xx|MOV ECX,[xxxx]|ECX = [xxxx];|xxxxは変...
||39 C8|CMP EAX,ECX|if (EAX >= ECX)||
||0F 8D 18 00 00 00|JGE $+24|goto 24バイト先|このとび先は...
|i = i + 1;|8B 05 xx xx xx xx|MOV EAX,[xxxx]|EAX = [xxxx]...
||8B 0D xx xx xx xx|MOV ECX,[xxxx]|ECX = [xxxx];|xxxxは変...
||01 C8|ADD EAX,ECX|EAX += ECX;||
||A3 xx xx xx xx|MOV [xxxx],EAX|[xxxx] = EAX;|xxxxは変数...
|}|E9 D4 FF FF FF|JMP $-44|goto 44バイト前|これは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-03aでは変数iなどはvar[]の中にあり、つまりこれはメモリ...
-機械語は覚えるのが容易ではないので、コード表などから調べ...
--簡易版のコード表: [[text0006a]]
** (12) なぜ32ビットコードにしたのか?
-今は64ビット対応のCPUがかなり普及して、OSやコンパイラも6...
-64ビット化することで大きく変わるのは、使えるレジスタの数...
-仮に割り付けを十分に頑張ったとして、レジスタが倍増すると...
-また64ビットの機械語はややこしいことになっているので、32...
-もちろん32ビットのJITコンパイラが書けるようになってから...
** 次回に続く
-次回: [[text0015]]
*こめんと欄
#comment
終了行:
* 川合のプログラミング言語自作のためのテキスト#0005
-(by [[K]], 2019.06.29)
** (10) TJ-03a
-お待たせしました。さあついにJITコンパイラです。プログラ...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <windows.h>
typedef unsigned char *String; // こう書くと String は u...
void loadText(int argc, const char **argv, unsigned char...
int isAlphabet(unsigned char c) → TL-2cと同じなので省略
int lexer(String s, String b, String t[]) → TL-2cと同じ...
enum { EAX = 0, ECX = 1 }; // 定数宣言.
void sub_print(int i)
{
printf("%d\n", i);
}
void sub_time()
{
printf("time=%.3f[sec]\n", clock() / (double) CLOCKS...
}
int put32(unsigned char *q, int i)
{
q[0] = i & 0xff;
q[1] = (i >> 8) & 0xff;
q[2] = (i >> 16) & 0xff;
q[3] = (i >> 24) & 0xff;
return 4;
}
int main(int argc, const char **argv)
{
static String def[] = { ";", "=", "+", "-", "print",...
int i, vars = 0, pc, pc1, qc = 0, wqc = 0, wqc2 = 0,...
String t[1000], varName[256]; // トークンと変数名.
unsigned char txt[10000], buf[10000]; // ソースコー...
unsigned char *code = VirtualAlloc(0, 1024 * 1024, M...
void (*func)(); // funcは関数へのポインタ型の変数.
loadText(argc, argv, txt, 10000);
pc1 = lexer(txt, buf, t);
t[pc1] = t[pc1 + 1] = t[pc1 + 2] = t[pc1 + 3] = ""; ...
for (i = 0; def[i] != 0; i++)
varName[i] = def[i];
vars = i;
code[qc++] = 0x83; // SUB ESP,124. この命令でスタッ...
code[qc++] = 0xec;
code[qc++] = 0x7c;
for (pc = 0; pc < pc1; pc++) {
for (i = 0; i < vars; i++) { // 登録済みの中から...
if (strcmp(t[pc], varName[i]) == 0)
break;
}
if (i == vars) {
varName[i] = t[pc]; // 見つからなかったので...
var[i] = strtol(t[pc], 0, 0); // 初期値を設定.
vars++;
}
varNum[pc] = i;
}
for (pc = 0; pc < pc1; pc++) {
if (varNum[pc + 1] == 1 /* = */) { // 2単語目が"...
code[qc++] = 0x8b; // MOV EAX,[...]
code[qc++] = EAX * 8 + 0x05;
qc += put32(&code[qc], (int) &var[varNum[pc ...
if (varNum[pc + 3] == 0 /* ; */) { // 単純代...
} else if (varNum[pc + 3] == 2 /* + */ && va...
code[qc++] = 0x8b; // MOV ECX,[...]
code[qc++] = ECX * 8 + 0x05;
qc += put32(&code[qc], (int) &var[varNum...
code[qc++] = 0x01;
code[qc++] = 0xc8;
} else if (varNum[pc + 3] == 3 /* - */ && va...
code[qc++] = 0x8b; // MOV ECX,[...]
code[qc++] = ECX * 8 + 0x05;
qc += put32(&code[qc], (int) &var[varNum...
code[qc++] = 0x29;
code[qc++] = 0xc8;
} else
goto err;
code[qc++] = 0xa3; // MOV [...],EAX
qc += put32(&code[qc], (int) &var[varNum[pc]...
} else if (varNum[pc] == 5 /* while */ && varNum...
wqc = qc;
code[qc++] = 0x8b; // MOV EAX,[...]
code[qc++] = EAX * 8 + 0x05;
qc += put32(&code[qc], (int) &var[varNum[pc ...
code[qc++] = 0x8b; // MOV ECX,[...]
code[qc++] = ECX * 8 + 0x05;
qc += put32(&code[qc], (int) &var[varNum[pc ...
code[qc++] = 0x39;
code[qc++] = 0xc8;
code[qc++] = 0x0f;
code[qc++] = 0x8d;
qc += put32(&code[qc], 0);
wqc2 = qc;
pc += 7 - 1;
continue;
} else if (varNum[pc] == 10 /* } */) {
code[qc++] = 0xe9;
qc += put32(&code[qc], &code[wqc] - &code[qc...
put32(&code[wqc2 - 4], &code[qc] - &code[wqc...
continue;
} else if (varNum[pc] == 11 /* time */ && varNum...
code[qc++] = 0xe8;
qc += put32(&code[qc], (unsigned char *) sub...
} else if (varNum[pc] == 4 /* print */ && varNum...
code[qc++] = 0x8b; // MOV EAX,[...]
code[qc++] = EAX * 8 + 0x05;
qc += put32(&code[qc], (int) &var[varNum[pc ...
code[qc++] = 0x89;
code[qc++] = 0x04;
code[qc++] = 0x24;
code[qc++] = 0xe8;
qc += put32(&code[qc], (unsigned char *) sub...
} else
goto err;
while (varNum[pc] != 0 /* ; */)
pc++;
}
code[qc++] = 0x83; // ADD ESP,124. 最初に調整したス...
code[qc++] = 0xc4;
code[qc++] = 0x7c;
code[qc++] = 0xc3; // RET
func = (void *) code;
func();
exit(0);
err:
printf("syntax error : %s %s %s %s\n", t[pc], t[pc +...
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-03aはTL-3cやTL-3dと完全互換です。違うのは実行速度...
i = 0;
while (i < 100000000) {
i = i + 1;
}
print i;
time;
-実行速度を比較してみてください。TL-3dはTL-3cと比べて11.3...
-しかもこのJITコンパイラはTL-3dと比べてコードを複雑にしな...
-正直、プログラムの中身は良くわからないところがたくさんあ...
** (11) TJ-03aの説明
-機械語が得意でたまらないという奇特な人を除けば、さすがに...
-まず、上記のサンプルプログラムが、どんな機械語に変換され...
|ソースコード|生成された機械語(16進数)|機械語をアセンブラ...
||83 EC 7C|SUB ESP,124|ESP -= 124;|スタック領域に作業用ス...
|i = 0;|8B 05 xx xx xx xx|MOV EAX,[xxxxx]|EAX = [xxxx];|x...
||A3 xx xx xx xx|MOV [xxxx],EAX|[xxxx] = EAX;|xxxxは変数...
|while (i < 100000000) {|8B 05 xx xx xx xx|MOV EAX,[xxxx]...
||8B 0D xx xx xx xx|MOV ECX,[xxxx]|ECX = [xxxx];|xxxxは変...
||39 C8|CMP EAX,ECX|if (EAX >= ECX)||
||0F 8D 18 00 00 00|JGE $+24|goto 24バイト先|このとび先は...
|i = i + 1;|8B 05 xx xx xx xx|MOV EAX,[xxxx]|EAX = [xxxx]...
||8B 0D xx xx xx xx|MOV ECX,[xxxx]|ECX = [xxxx];|xxxxは変...
||01 C8|ADD EAX,ECX|EAX += ECX;||
||A3 xx xx xx xx|MOV [xxxx],EAX|[xxxx] = EAX;|xxxxは変数...
|}|E9 D4 FF FF FF|JMP $-44|goto 44バイト前|これは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-03aでは変数iなどはvar[]の中にあり、つまりこれはメモリ...
-機械語は覚えるのが容易ではないので、コード表などから調べ...
--簡易版のコード表: [[text0006a]]
** (12) なぜ32ビットコードにしたのか?
-今は64ビット対応のCPUがかなり普及して、OSやコンパイラも6...
-64ビット化することで大きく変わるのは、使えるレジスタの数...
-仮に割り付けを十分に頑張ったとして、レジスタが倍増すると...
-また64ビットの機械語はややこしいことになっているので、32...
-もちろん32ビットのJITコンパイラが書けるようになってから...
** 次回に続く
-次回: [[text0015]]
*こめんと欄
#comment
ページ名: