a21_txt02
をテンプレートにして作成
[
トップ
] [
新規
|
一覧
|
単語検索
|
最終更新
|
ヘルプ
]
開始行:
* 「10日くらいでできる!プログラミング言語自作入門」の続...
-(by [[K]], 2021.04.05)
** (1) はじめに
-このテキストは、「10日くらいでできる!プログラミング言語...
-このシリーズでは、言語に新規の命令を追加することは最小限...
-言語に独自の機能を加えていって言語を「進化」させていく話...
----
-もくじ(HL-1から順番に読んでいくとわかるように書いていま...
|ページ名|名前|行数|.exeの大きさ|説明|速度のめやす1|速度...
|[ [[a21_txt01]] ]|HL-1~HL-9a|RIGHT:49行~772行|RIGHT:6....
||||||||
|[[a21_txt02]]|HL-11|RIGHT:692行|RIGHT:14.5KB|x86のJITコ...
|[[a21_txt02_1a]]|HL-11a|RIGHT:707行|RIGHT:15.0KB|簡単な...
|[[a21_txt02_2]]|HL-12|RIGHT:745行|RIGHT:16.0KB|ループ速...
|[[a21_txt02_2a]]|HL-12a|RIGHT:766行|RIGHT:16.5KB|codedum...
|[[a21_txt02_3]]|HL-13|RIGHT:827行|RIGHT:21.0KB|配列以外...
|[[a21_txt02_3a]]|HL-13a|RIGHT:819行|RIGHT:21.0KB|配列もJ...
|[[a21_txt02_4]]|HL-14|RIGHT:865行|RIGHT:21.5KB|レジスタ...
|[[a21_txt02_4a]]|HL-14a|RIGHT:906行|RIGHT:22.5KB|レジス...
|[[a21_txt02_5]]|HL-15|RIGHT:942行|RIGHT:23.0KB|無駄なメ...
|[[a21_txt02_5a]]|HL-15a|RIGHT:1012行|RIGHT:23.5KB|即値指...
|[[a21_txt02_6]]|HL-16|RIGHT:1065行|RIGHT:24.0KB|コンパイ...
|[[a21_txt02_6a]]|HL-16a|RIGHT:1081行|RIGHT:24.5KB|変数参...
||||||||
|[[a21_txt02_6b]]|HL-16b|RIGHT:1284行|RIGHT:28.5KB|(JIT...
||||||||
|[[a21_txt02_7]]|HL-17|RIGHT:741行|RIGHT:25.0KB|x64に対応...
|[[a21_txt02_7a]]|HL-17a|RIGHT:756行|RIGHT:26.0KB|簡単な...
|[[a21_txt02_8]]|HL-18|RIGHT:795行|RIGHT:27.0KB|ループ速...
|[[a21_txt02_8a]]|HL-18a|RIGHT:816行|RIGHT:27.5KB|codedum...
|[[a21_txt02_9]]|HL-19|RIGHT:893行|RIGHT:35.5KB|配列以外...
|[[a21_txt02_9a]]|HL-19a|RIGHT:899行|RIGHT:36.0KB|配列もJ...
|[[a21_txt02_10]]|HL-20|RIGHT:964行|RIGHT:36.0KB|レジスタ...
|[[a21_txt02_10a]]|HL-20a|RIGHT:1014行|RIGHT:37.0KB|レジ...
|[[a21_txt02_10b]]|HL-20b|RIGHT:1035行|RIGHT:37.5KB|アラ...
|[[a21_txt02_11]]|HL-21|RIGHT:1070行|RIGHT:38.5KB|無駄な...
|[[a21_txt02_11a]]|HL-21a|RIGHT:1151行|RIGHT:40.5KB|即値...
|[[a21_txt02_12]]|HL-22|RIGHT:1204行|RIGHT:41.0KB|コンパ...
|[[a21_txt02_12a]]|HL-22a|RIGHT:1223行|RIGHT:|変数参照回...
||||||||
|[[a21_txt02_12b]]|HL-22b|RIGHT:|RIGHT:|(JITではない)「...
||||||||
|||||C言語ソースを出力して、トランスコンパイラにしてみる|||
-速度のめやす
--速度のめやす1: 10億回ループをgcc-O3(32bit)でやった場合...
--速度のめやす2: mandel.cをgcc-O3(32bit)でやった場合との...
-HL-14~16(HL-20~22)は最適化の話ばかりになります。そこ...
--(JITコンパイラ化は、第一に高速化のためにやることなので...
-たとえばHL-11のあとにHL-11aがあります。これは1回分の内容...
** (2) HL-11
-ということで、HL-11について説明したいと思います。HL-1か...
-HL-11の基本方針はこうです。
--[1]compile()関数では、内部コード出力をやめて、代わりにx...
--[2]exec()関数は不要なので削除。
--[3]機械語を出力するにあたって、putIc()のままでは全然便...
--[4]それにともない、putIc()関数は期待通りには動かなくな...
--[5]とりあえず、単純代入命令とprint命令だけputIcX86()に...
-こうすることで、HL-9aをJITコンパイラ型の言語に改造します...
-この改造をすると、言語はCPUに依存するようになります。''x...
-しかしそれでも、やるだけの価値はあります。それほど高速に...
-説明が前後していますが「JITコンパイラ」というのは「Just-...
#include <acl.c>
typedef unsigned char *String; // こう書くと String は u...
int loadText(String path, String t, int siz) → HL-4と同...
////////////////////////////////////////////////////////...
#define MAX_TC 1000 // トークンコードの最大値.
String ts[MAX_TC + 1]; // トークンの内容(文字列)を記憶.
int tl[MAX_TC + 1]; // トークンの長さ.
unsigned char tcBuf[(MAX_TC + 1) * 10]; // トークン1つ当...
int tcs = 0, tcb = 0;
AInt var[MAX_TC + 1]; // 変数.
int getTc(String s, int len) → HL-8aと同じなので省略
////////////////////////////////////////////////////////...
int isAlphabetOrNumber(unsigned char c) → HL-2と同じなの...
int lexer(String s, int tc[]) → HL-9aと同じなので省略
int tc[10000]; // トークンコード.
enum { TcSemi = 0, TcDot, TcWiCard, Tc0, Tc1, Tc2, Tc3, ...
TcEEq, TcNEq, TcLt, TcGe, TcLe, TcGt, TcPlus, TcMinu...
TcComma, TcExpr, TcExpr0, TcTmp0, TcTmp1, TcTmp2, Tc...
char tcInit[] = "; . !!* 0 1 2 3 4 5 6 7 8 ( ) [ ] { } =...
////////////////////////////////////////////////////////...
int phrCmp_tc[32 * 100], ppc1, wpc[9], wpc1[9]; // ppc1:...
int phrCmp(int pid, String phr, int pc) → HL-7と同じなの...
////////////////////////////////////////////////////////...
typedef AInt *IntP; // こう書くと IntP は AInt * の代わ...
enum { OpCpy = 0, OpCeq, OpCne, OpClt, OpCge, OpCle, OpC...
OpAdd1, OpNeg, OpGoto, OpJeq, OpJne, OpJlt, OpJge, O...
OpPrints, OpAryNew, OpAryInit, OpArySet, OpAryGet, O...
OpXorShift, OpGetPix, OpFilRct0, OpPrm, OpF16Sin, Op...
unsigned char *ic, *icq; // ic[]:内部コード、icq:ic[]へ...
void putIc(int op, IntP p0, IntP p1, IntP p2, IntP p3) ...
{
printf("putIc: error\n");
exit(1);
}
int getHex(int c) // 16進数に使える文字ならそれを0~15の...
{
if ('0' <= c && c <= '9') return c - '0';
if ('a' <= c && c <= 'f') return c - 'a' + 10;
if ('A' <= c && c <= 'F') return c - 'A' + 10;
return -1;
}
int get32(unsigned char *p)
{
return p[0] + p[1] * 256 + p[2] * 65536 + p[3] * 167...
}
void put32(unsigned char *p, int i)
{
p[0] = i & 0xff; // 1バイト目に、ビット0~7...
p[1] = (i >> 8) & 0xff; // 2バイト目に、ビット8~15...
p[2] = (i >> 16) & 0xff; // 3バイト目に、ビット16~2...
p[3] = (i >> 24) & 0xff; // 4バイト目に、ビット24~3...
}
void putIcX86_sub(String s, IntP a[])
{
int i, j, k;
for (i = 0; s[i] != 0; ) {
if (s[i] == ' ' || s[i] == '\t' || s[i] == '_' |...
i++;
} else if (getHex(s[i]) >= 0 && getHex(s[i + 1])...
*icq = getHex(s[i]) * 16 + getHex(s[i + 1]);
i += 2;
icq++;
} else if (s[i] == '%') {
j = s[i + 1] - '0';
if (s[i + 2] == 'm') { // mod r/m.
k = s[i + 3] - '0';
*icq = 0x05 + k * 8;
put32(icq + 1, (int) a[j]);
icq += 5;
i += 4;
continue;
}
if (s[i + 2] == 'i') { // int.
put32(icq, (int) a[j]);
icq += 4;
}
if (s[i + 2] == 'c') { // char.
*icq = (int) a[j];
icq++;
}
if (s[i + 2] == 'r') { // relative.
put32(icq, (int) a[j] - (int) (icq + 4));
icq += 4;
}
i += 3;
} else {
printf("putIcX86: error: '%s'\n", s);
exit(1);
}
}
}
void putIcX86(String s, IntP p0, IntP p1, IntP p2, IntP ...
{
IntP a[4];
a[0] = p0;
a[1] = p1;
a[2] = p2;
a[3] = p3;
putIcX86_sub(s, a);
}
////////////////////////////////////////////////////////...
char tmp_flag[10]; // 一時変数の利用状況を管理.
int tmpAlloc() → HL-7と同じなので省略
void tmpFree(int i) → HL-7と同じなので省略
////////////////////////////////////////////////////////...
void sub_print(int i)
{
printf("%d\n", i);
}
////////////////////////////////////////////////////////...
int epc, epc1; // exprのためのpcとpc1.
int exprSub(int priority); // exprSub1()が参照するので、...
int expr(int j);
int phrCmpPutIc(int pid, String phr, int pc, int *pi, in...
int exprSub1(int i, int priority, int op) → HL-7と同じな...
int exprSub(int priority) → HL-9と同じなので省略
int expr(int j) → HL-7と同じなので省略
////////////////////////////////////////////////////////...
enum { IfTrue = 0, IfFalse = 1 };
void ifgoto(int i, int not, int label) → HL-8と同じなの...
int tmpLabelNo;
int tmpLabelAlloc() → HL-8と同じなので省略
#define BInfSiz 10
int binf[BInfSiz * 100], bd, lbd; // binf:block-info, bd...
enum { BlkIf = 1, BlkFor, BlkMain }; // BlkMainをHL-9aで...
enum { IfLabel0 = 1, IfLabel1 };
enum { ForLopBgn = 1, ForCont, ForBrk, ForLbd0, ForWpc01...
int phrCmpPutIc(int pid, String phr, int pc, int *pi, in...
{
if (phrCmp(pid, phr, pc)) {
printf("phrCmpPutIc: error\n");
exit(1);
}
return 0;
}
int phrCmpPutIcX86(int pid, String phr, int pc, int *pi,...
{
int e[9], i;
if (phrCmp(pid, phr, pc)) {
e[0] = e[1] = e[2] = e[3] = e[4] = e[5] = e[6] =...
for (i = 0; i < lenExpr; i++) {
e[i] = expr(i);
}
for (i = 0; i < lenExpr; i++) {
putIcX86("8b_%0m0; 89_44_24_%1c;", &var[e[i]], (Int...
}
putIcX86("e8_%0r;", sub, 0, 0, 0);
for (i = 0; i < lenExpr; i++) {
if (e[i] < 0) {
*err = -1;
}
tmpFree(e[i]);
}
if (pi != 0) {
*pi = tmpAlloc();
putIcX86("89_%0m0;", &var[*pi], 0, 0, 0);
}
return 1;
}
return 0;
}
////////////////////////////////////////////////////////...
int compile(String s)
{
int pc, pc1, i, j;
! unsigned char *icq1, *icp;
pc1 = lexer(s, tc);
tc[pc1++] = TcSemi; // 末尾に「;」を付け忘れることが...
tc[pc1] = tc[pc1 + 1] = tc[pc1 + 2] = tc[pc1 + 3] = ...
icq = ic;
+ putIcX86("60; 83_ec_7c;", 0, 0, 0, 0); // PUSHAD(); ...
for (i = 0; i < 10; i++) {
tmp_flag[i] = 0;
}
tmpLabelNo = 0;
bd = lbd = 0;
for (pc = 0; pc < pc1; ) { // コンパイル開始.
int e0 = 0, e2 = 0;
if (phrCmp( 1, "!!*0 = !!*1;", pc)) { // 単純代入.
! putIcX86("8b_%1m0; 89_%0m0;", &var[tc[wpc[0]...
} else if (phrCmp(10, "!!*0 = !!*1 + 1; if (!!*2...
(中略)
! } else if (phrCmpPutIcX86(4, "print !!**0;", pc,...
(中略)
}
if (bd > 0) {
printf("block nesting error (bd=%d, lbd=%d, pc=%...
return -1;
}
! putIcX86("83_c4_7c; 61; c3;", 0, 0, 0, 0); // ADD(ES...
icq1 = icq;
// ここにあった「goto先の設定」はいったん全部削除.
return icq1 - ic;
err:
printf("syntax error : %s %s %s %s\n", ts[tc[pc]], t...
return -1;
}
// このあたりにあったexec()関数の記述はすべて削除.
// ついでに変数winの宣言も削除.
int run(String s)
{
if (compile(s) < 0)
return 1;
+ void (*func)() = (void (*)()) ic;
! func();
return 0;
}
// 以下のmallocRWX()はWindows用の記述. Linux系のOSの場合...
#include <windows.h>
void *mallocRWX(int siz)
{
return VirtualAlloc(0, siz, MEM_COMMIT, PAGE_EXECUTE...
}
////////////////////////////////////////////////////////...
void aMain()
{
unsigned char txt[10000];
int i;
+ ic = mallocRWX(1024 * 1024); // 1MB
lexer(tcInit, tc);
(中略)
}
-プログラムは692行になりました。このHL-11では、printと単...
>print 1
1
>print 2
2
>a=4
>print a
4
-とてもつまらなくなったのではありますが、しかしこれは全部...
** (3) HL-11に関する重要な説明
-このHL-11は、x86の32bitモード専用で書かれています。した...
-「なぜ現在主流の64bitではなく、32bitにしたのか?」ですが...
--より具体的に言うと、x64の何が難しいのかというと、JITコ...
--もう一つは、x64ではOSごとに関数呼び出し規則が違っていて...
--そして、64bitモードにしてもしなくても、速度はほとんど変...
--ということで、64bitモード対応は将来の課題ということにし...
** (4) 今回の改造のあらまし
-今回からic[ ]に機械語を入れて実行することになるのですが...
-しかしこれができなければ、そもそもJITコンパイラなんて実...
-Windowsの場合は、上記のプログラムのように
#include <windows.h>
void *mallocRWX(int siz)
{
return VirtualAlloc(0, siz, MEM_COMMIT, PAGE_EXECUTE...
}
-とすることで可能になります。
-LinuxなどのPOSIX系のOSでは、以下のようにやればできます。
#include <unistd.h>
#include <sys/mman.h>
void *mallocRWX(int siz)
{
return mmap(0, siz, PROT_READ | PROT_WRITE | PROT_EX...
}
-HL-11ではこのmallocRWX()を呼び出して、返値をicに代入して...
-ic[ ]の中に機械語を書き込んだ後は、
void (*func)() = (void (*)()) ic;
func();
-これで呼び出しています(これはrun()関数の中に書いてあり...
-2行目はただの関数呼び出しです。こうすることで、ic[ ]に書...
-ic[ ]に適切な機械語を書き込むのは、compile()関数の仕事で...
-なお、ic[ ]に機械語を書き込む際には、1バイトずつ書き込む...
** (5) 生成している機械語の説明(putIcX86()やphrCmpPutIcX...
-ここを読んでいる多くの人は、おそらくx86の機械語なんて知...
----
-compile()では、ic[ ]の冒頭に、
putIcX86("60; 83_ec_7c;", 0, 0, 0, 0); // PUSHAD(); ...
-を書きこんでいます。また末尾には、
putIcX86("83_c4_7c; 61; c3;", 0, 0, 0, 0); // ADD(ES...
-を書き込んでいます。・・・まずはこのあたりから説明します。
-まず命令「60」は、1バイトの命令で、EAX~EDIの8つのレジス...
-命令「83_ec_7c」は3バイトの命令で、ESP=ESP-124;を計算さ...
-ここでputIcX86()関数についても説明します。最初に文字列で...
----
-単純代入命令では、
putIcX86("8b_%1m0; 89_%0m0;", &var[tc[wpc[0]]], &var...
-という機械語を使っています。ちなみにここはHL-9aでは
putIc(OpCpy, &var[tc[wpc[0]]], &var[tc[wpc[1]]], 0,...
-になっていました。第二引数以降は完全に同じになっています...
-命令「8b」と「89」はどちらもアセンブラではMOV命令と呼ば...
-次の%1m0の意味するところは、まず1で「追加引数の[1]を参照...
-最後の0は、オペランド内の第二引数欄に0を指定せよ、という...
-8b命令や89命令では、命令コードの直後に「mod r/mバイト」...
-この例では第二オペランドは0なので、EAXレジスタを指定した...
-ということでまとめると、最初の8b命令で、 EAX = var[tc[wp...
----
-次はprint命令です。phrCmpPutIcX86()関数によって、こんな...
putIcX86("8b_%0m0; 89_44_24_%1c;", &var[e[0]], 0, 0, 0);
putIcX86("e8_%0r", sub_print, 0, 0, 0);
-1行目で、print命令の引数をEAXに読み込んだ後、[ESP+0] = E...
--このmod r/mについての詳しいことは、たとえば https://www...
-2行目のe8命令は、アセンブラではCALL命令と言われているも...
----
-これですべてです。結局、60, 61, 83, c3, 8b, 89, e8の7種...
** (6) 機械語を調べるためのリンク集
-https://www.wdic.org/w/SCI/ModR/M : mod R/Mについて
-http://ref.x86asm.net/coder32.html : x86の機械語の一覧
** 次回に続く
-次回: [[a21_txt02_1a]]
*こめんと欄
#comment
終了行:
* 「10日くらいでできる!プログラミング言語自作入門」の続...
-(by [[K]], 2021.04.05)
** (1) はじめに
-このテキストは、「10日くらいでできる!プログラミング言語...
-このシリーズでは、言語に新規の命令を追加することは最小限...
-言語に独自の機能を加えていって言語を「進化」させていく話...
----
-もくじ(HL-1から順番に読んでいくとわかるように書いていま...
|ページ名|名前|行数|.exeの大きさ|説明|速度のめやす1|速度...
|[ [[a21_txt01]] ]|HL-1~HL-9a|RIGHT:49行~772行|RIGHT:6....
||||||||
|[[a21_txt02]]|HL-11|RIGHT:692行|RIGHT:14.5KB|x86のJITコ...
|[[a21_txt02_1a]]|HL-11a|RIGHT:707行|RIGHT:15.0KB|簡単な...
|[[a21_txt02_2]]|HL-12|RIGHT:745行|RIGHT:16.0KB|ループ速...
|[[a21_txt02_2a]]|HL-12a|RIGHT:766行|RIGHT:16.5KB|codedum...
|[[a21_txt02_3]]|HL-13|RIGHT:827行|RIGHT:21.0KB|配列以外...
|[[a21_txt02_3a]]|HL-13a|RIGHT:819行|RIGHT:21.0KB|配列もJ...
|[[a21_txt02_4]]|HL-14|RIGHT:865行|RIGHT:21.5KB|レジスタ...
|[[a21_txt02_4a]]|HL-14a|RIGHT:906行|RIGHT:22.5KB|レジス...
|[[a21_txt02_5]]|HL-15|RIGHT:942行|RIGHT:23.0KB|無駄なメ...
|[[a21_txt02_5a]]|HL-15a|RIGHT:1012行|RIGHT:23.5KB|即値指...
|[[a21_txt02_6]]|HL-16|RIGHT:1065行|RIGHT:24.0KB|コンパイ...
|[[a21_txt02_6a]]|HL-16a|RIGHT:1081行|RIGHT:24.5KB|変数参...
||||||||
|[[a21_txt02_6b]]|HL-16b|RIGHT:1284行|RIGHT:28.5KB|(JIT...
||||||||
|[[a21_txt02_7]]|HL-17|RIGHT:741行|RIGHT:25.0KB|x64に対応...
|[[a21_txt02_7a]]|HL-17a|RIGHT:756行|RIGHT:26.0KB|簡単な...
|[[a21_txt02_8]]|HL-18|RIGHT:795行|RIGHT:27.0KB|ループ速...
|[[a21_txt02_8a]]|HL-18a|RIGHT:816行|RIGHT:27.5KB|codedum...
|[[a21_txt02_9]]|HL-19|RIGHT:893行|RIGHT:35.5KB|配列以外...
|[[a21_txt02_9a]]|HL-19a|RIGHT:899行|RIGHT:36.0KB|配列もJ...
|[[a21_txt02_10]]|HL-20|RIGHT:964行|RIGHT:36.0KB|レジスタ...
|[[a21_txt02_10a]]|HL-20a|RIGHT:1014行|RIGHT:37.0KB|レジ...
|[[a21_txt02_10b]]|HL-20b|RIGHT:1035行|RIGHT:37.5KB|アラ...
|[[a21_txt02_11]]|HL-21|RIGHT:1070行|RIGHT:38.5KB|無駄な...
|[[a21_txt02_11a]]|HL-21a|RIGHT:1151行|RIGHT:40.5KB|即値...
|[[a21_txt02_12]]|HL-22|RIGHT:1204行|RIGHT:41.0KB|コンパ...
|[[a21_txt02_12a]]|HL-22a|RIGHT:1223行|RIGHT:|変数参照回...
||||||||
|[[a21_txt02_12b]]|HL-22b|RIGHT:|RIGHT:|(JITではない)「...
||||||||
|||||C言語ソースを出力して、トランスコンパイラにしてみる|||
-速度のめやす
--速度のめやす1: 10億回ループをgcc-O3(32bit)でやった場合...
--速度のめやす2: mandel.cをgcc-O3(32bit)でやった場合との...
-HL-14~16(HL-20~22)は最適化の話ばかりになります。そこ...
--(JITコンパイラ化は、第一に高速化のためにやることなので...
-たとえばHL-11のあとにHL-11aがあります。これは1回分の内容...
** (2) HL-11
-ということで、HL-11について説明したいと思います。HL-1か...
-HL-11の基本方針はこうです。
--[1]compile()関数では、内部コード出力をやめて、代わりにx...
--[2]exec()関数は不要なので削除。
--[3]機械語を出力するにあたって、putIc()のままでは全然便...
--[4]それにともない、putIc()関数は期待通りには動かなくな...
--[5]とりあえず、単純代入命令とprint命令だけputIcX86()に...
-こうすることで、HL-9aをJITコンパイラ型の言語に改造します...
-この改造をすると、言語はCPUに依存するようになります。''x...
-しかしそれでも、やるだけの価値はあります。それほど高速に...
-説明が前後していますが「JITコンパイラ」というのは「Just-...
#include <acl.c>
typedef unsigned char *String; // こう書くと String は u...
int loadText(String path, String t, int siz) → HL-4と同...
////////////////////////////////////////////////////////...
#define MAX_TC 1000 // トークンコードの最大値.
String ts[MAX_TC + 1]; // トークンの内容(文字列)を記憶.
int tl[MAX_TC + 1]; // トークンの長さ.
unsigned char tcBuf[(MAX_TC + 1) * 10]; // トークン1つ当...
int tcs = 0, tcb = 0;
AInt var[MAX_TC + 1]; // 変数.
int getTc(String s, int len) → HL-8aと同じなので省略
////////////////////////////////////////////////////////...
int isAlphabetOrNumber(unsigned char c) → HL-2と同じなの...
int lexer(String s, int tc[]) → HL-9aと同じなので省略
int tc[10000]; // トークンコード.
enum { TcSemi = 0, TcDot, TcWiCard, Tc0, Tc1, Tc2, Tc3, ...
TcEEq, TcNEq, TcLt, TcGe, TcLe, TcGt, TcPlus, TcMinu...
TcComma, TcExpr, TcExpr0, TcTmp0, TcTmp1, TcTmp2, Tc...
char tcInit[] = "; . !!* 0 1 2 3 4 5 6 7 8 ( ) [ ] { } =...
////////////////////////////////////////////////////////...
int phrCmp_tc[32 * 100], ppc1, wpc[9], wpc1[9]; // ppc1:...
int phrCmp(int pid, String phr, int pc) → HL-7と同じなの...
////////////////////////////////////////////////////////...
typedef AInt *IntP; // こう書くと IntP は AInt * の代わ...
enum { OpCpy = 0, OpCeq, OpCne, OpClt, OpCge, OpCle, OpC...
OpAdd1, OpNeg, OpGoto, OpJeq, OpJne, OpJlt, OpJge, O...
OpPrints, OpAryNew, OpAryInit, OpArySet, OpAryGet, O...
OpXorShift, OpGetPix, OpFilRct0, OpPrm, OpF16Sin, Op...
unsigned char *ic, *icq; // ic[]:内部コード、icq:ic[]へ...
void putIc(int op, IntP p0, IntP p1, IntP p2, IntP p3) ...
{
printf("putIc: error\n");
exit(1);
}
int getHex(int c) // 16進数に使える文字ならそれを0~15の...
{
if ('0' <= c && c <= '9') return c - '0';
if ('a' <= c && c <= 'f') return c - 'a' + 10;
if ('A' <= c && c <= 'F') return c - 'A' + 10;
return -1;
}
int get32(unsigned char *p)
{
return p[0] + p[1] * 256 + p[2] * 65536 + p[3] * 167...
}
void put32(unsigned char *p, int i)
{
p[0] = i & 0xff; // 1バイト目に、ビット0~7...
p[1] = (i >> 8) & 0xff; // 2バイト目に、ビット8~15...
p[2] = (i >> 16) & 0xff; // 3バイト目に、ビット16~2...
p[3] = (i >> 24) & 0xff; // 4バイト目に、ビット24~3...
}
void putIcX86_sub(String s, IntP a[])
{
int i, j, k;
for (i = 0; s[i] != 0; ) {
if (s[i] == ' ' || s[i] == '\t' || s[i] == '_' |...
i++;
} else if (getHex(s[i]) >= 0 && getHex(s[i + 1])...
*icq = getHex(s[i]) * 16 + getHex(s[i + 1]);
i += 2;
icq++;
} else if (s[i] == '%') {
j = s[i + 1] - '0';
if (s[i + 2] == 'm') { // mod r/m.
k = s[i + 3] - '0';
*icq = 0x05 + k * 8;
put32(icq + 1, (int) a[j]);
icq += 5;
i += 4;
continue;
}
if (s[i + 2] == 'i') { // int.
put32(icq, (int) a[j]);
icq += 4;
}
if (s[i + 2] == 'c') { // char.
*icq = (int) a[j];
icq++;
}
if (s[i + 2] == 'r') { // relative.
put32(icq, (int) a[j] - (int) (icq + 4));
icq += 4;
}
i += 3;
} else {
printf("putIcX86: error: '%s'\n", s);
exit(1);
}
}
}
void putIcX86(String s, IntP p0, IntP p1, IntP p2, IntP ...
{
IntP a[4];
a[0] = p0;
a[1] = p1;
a[2] = p2;
a[3] = p3;
putIcX86_sub(s, a);
}
////////////////////////////////////////////////////////...
char tmp_flag[10]; // 一時変数の利用状況を管理.
int tmpAlloc() → HL-7と同じなので省略
void tmpFree(int i) → HL-7と同じなので省略
////////////////////////////////////////////////////////...
void sub_print(int i)
{
printf("%d\n", i);
}
////////////////////////////////////////////////////////...
int epc, epc1; // exprのためのpcとpc1.
int exprSub(int priority); // exprSub1()が参照するので、...
int expr(int j);
int phrCmpPutIc(int pid, String phr, int pc, int *pi, in...
int exprSub1(int i, int priority, int op) → HL-7と同じな...
int exprSub(int priority) → HL-9と同じなので省略
int expr(int j) → HL-7と同じなので省略
////////////////////////////////////////////////////////...
enum { IfTrue = 0, IfFalse = 1 };
void ifgoto(int i, int not, int label) → HL-8と同じなの...
int tmpLabelNo;
int tmpLabelAlloc() → HL-8と同じなので省略
#define BInfSiz 10
int binf[BInfSiz * 100], bd, lbd; // binf:block-info, bd...
enum { BlkIf = 1, BlkFor, BlkMain }; // BlkMainをHL-9aで...
enum { IfLabel0 = 1, IfLabel1 };
enum { ForLopBgn = 1, ForCont, ForBrk, ForLbd0, ForWpc01...
int phrCmpPutIc(int pid, String phr, int pc, int *pi, in...
{
if (phrCmp(pid, phr, pc)) {
printf("phrCmpPutIc: error\n");
exit(1);
}
return 0;
}
int phrCmpPutIcX86(int pid, String phr, int pc, int *pi,...
{
int e[9], i;
if (phrCmp(pid, phr, pc)) {
e[0] = e[1] = e[2] = e[3] = e[4] = e[5] = e[6] =...
for (i = 0; i < lenExpr; i++) {
e[i] = expr(i);
}
for (i = 0; i < lenExpr; i++) {
putIcX86("8b_%0m0; 89_44_24_%1c;", &var[e[i]], (Int...
}
putIcX86("e8_%0r;", sub, 0, 0, 0);
for (i = 0; i < lenExpr; i++) {
if (e[i] < 0) {
*err = -1;
}
tmpFree(e[i]);
}
if (pi != 0) {
*pi = tmpAlloc();
putIcX86("89_%0m0;", &var[*pi], 0, 0, 0);
}
return 1;
}
return 0;
}
////////////////////////////////////////////////////////...
int compile(String s)
{
int pc, pc1, i, j;
! unsigned char *icq1, *icp;
pc1 = lexer(s, tc);
tc[pc1++] = TcSemi; // 末尾に「;」を付け忘れることが...
tc[pc1] = tc[pc1 + 1] = tc[pc1 + 2] = tc[pc1 + 3] = ...
icq = ic;
+ putIcX86("60; 83_ec_7c;", 0, 0, 0, 0); // PUSHAD(); ...
for (i = 0; i < 10; i++) {
tmp_flag[i] = 0;
}
tmpLabelNo = 0;
bd = lbd = 0;
for (pc = 0; pc < pc1; ) { // コンパイル開始.
int e0 = 0, e2 = 0;
if (phrCmp( 1, "!!*0 = !!*1;", pc)) { // 単純代入.
! putIcX86("8b_%1m0; 89_%0m0;", &var[tc[wpc[0]...
} else if (phrCmp(10, "!!*0 = !!*1 + 1; if (!!*2...
(中略)
! } else if (phrCmpPutIcX86(4, "print !!**0;", pc,...
(中略)
}
if (bd > 0) {
printf("block nesting error (bd=%d, lbd=%d, pc=%...
return -1;
}
! putIcX86("83_c4_7c; 61; c3;", 0, 0, 0, 0); // ADD(ES...
icq1 = icq;
// ここにあった「goto先の設定」はいったん全部削除.
return icq1 - ic;
err:
printf("syntax error : %s %s %s %s\n", ts[tc[pc]], t...
return -1;
}
// このあたりにあったexec()関数の記述はすべて削除.
// ついでに変数winの宣言も削除.
int run(String s)
{
if (compile(s) < 0)
return 1;
+ void (*func)() = (void (*)()) ic;
! func();
return 0;
}
// 以下のmallocRWX()はWindows用の記述. Linux系のOSの場合...
#include <windows.h>
void *mallocRWX(int siz)
{
return VirtualAlloc(0, siz, MEM_COMMIT, PAGE_EXECUTE...
}
////////////////////////////////////////////////////////...
void aMain()
{
unsigned char txt[10000];
int i;
+ ic = mallocRWX(1024 * 1024); // 1MB
lexer(tcInit, tc);
(中略)
}
-プログラムは692行になりました。このHL-11では、printと単...
>print 1
1
>print 2
2
>a=4
>print a
4
-とてもつまらなくなったのではありますが、しかしこれは全部...
** (3) HL-11に関する重要な説明
-このHL-11は、x86の32bitモード専用で書かれています。した...
-「なぜ現在主流の64bitではなく、32bitにしたのか?」ですが...
--より具体的に言うと、x64の何が難しいのかというと、JITコ...
--もう一つは、x64ではOSごとに関数呼び出し規則が違っていて...
--そして、64bitモードにしてもしなくても、速度はほとんど変...
--ということで、64bitモード対応は将来の課題ということにし...
** (4) 今回の改造のあらまし
-今回からic[ ]に機械語を入れて実行することになるのですが...
-しかしこれができなければ、そもそもJITコンパイラなんて実...
-Windowsの場合は、上記のプログラムのように
#include <windows.h>
void *mallocRWX(int siz)
{
return VirtualAlloc(0, siz, MEM_COMMIT, PAGE_EXECUTE...
}
-とすることで可能になります。
-LinuxなどのPOSIX系のOSでは、以下のようにやればできます。
#include <unistd.h>
#include <sys/mman.h>
void *mallocRWX(int siz)
{
return mmap(0, siz, PROT_READ | PROT_WRITE | PROT_EX...
}
-HL-11ではこのmallocRWX()を呼び出して、返値をicに代入して...
-ic[ ]の中に機械語を書き込んだ後は、
void (*func)() = (void (*)()) ic;
func();
-これで呼び出しています(これはrun()関数の中に書いてあり...
-2行目はただの関数呼び出しです。こうすることで、ic[ ]に書...
-ic[ ]に適切な機械語を書き込むのは、compile()関数の仕事で...
-なお、ic[ ]に機械語を書き込む際には、1バイトずつ書き込む...
** (5) 生成している機械語の説明(putIcX86()やphrCmpPutIcX...
-ここを読んでいる多くの人は、おそらくx86の機械語なんて知...
----
-compile()では、ic[ ]の冒頭に、
putIcX86("60; 83_ec_7c;", 0, 0, 0, 0); // PUSHAD(); ...
-を書きこんでいます。また末尾には、
putIcX86("83_c4_7c; 61; c3;", 0, 0, 0, 0); // ADD(ES...
-を書き込んでいます。・・・まずはこのあたりから説明します。
-まず命令「60」は、1バイトの命令で、EAX~EDIの8つのレジス...
-命令「83_ec_7c」は3バイトの命令で、ESP=ESP-124;を計算さ...
-ここでputIcX86()関数についても説明します。最初に文字列で...
----
-単純代入命令では、
putIcX86("8b_%1m0; 89_%0m0;", &var[tc[wpc[0]]], &var...
-という機械語を使っています。ちなみにここはHL-9aでは
putIc(OpCpy, &var[tc[wpc[0]]], &var[tc[wpc[1]]], 0,...
-になっていました。第二引数以降は完全に同じになっています...
-命令「8b」と「89」はどちらもアセンブラではMOV命令と呼ば...
-次の%1m0の意味するところは、まず1で「追加引数の[1]を参照...
-最後の0は、オペランド内の第二引数欄に0を指定せよ、という...
-8b命令や89命令では、命令コードの直後に「mod r/mバイト」...
-この例では第二オペランドは0なので、EAXレジスタを指定した...
-ということでまとめると、最初の8b命令で、 EAX = var[tc[wp...
----
-次はprint命令です。phrCmpPutIcX86()関数によって、こんな...
putIcX86("8b_%0m0; 89_44_24_%1c;", &var[e[0]], 0, 0, 0);
putIcX86("e8_%0r", sub_print, 0, 0, 0);
-1行目で、print命令の引数をEAXに読み込んだ後、[ESP+0] = E...
--このmod r/mについての詳しいことは、たとえば https://www...
-2行目のe8命令は、アセンブラではCALL命令と言われているも...
----
-これですべてです。結局、60, 61, 83, c3, 8b, 89, e8の7種...
** (6) 機械語を調べるためのリンク集
-https://www.wdic.org/w/SCI/ModR/M : mod R/Mについて
-http://ref.x86asm.net/coder32.html : x86の機械語の一覧
** 次回に続く
-次回: [[a21_txt02_1a]]
*こめんと欄
#comment
ページ名: