* 「10日くらいでできる!プログラミング言語自作入門」の続編#1-9a
-(by [[K]], 2021.05.11)

** (1) HL-19a
-HL-19で保留にしてきた配列の機能をJITコンパラ対応させます。これでkcube.cやinvader.cも動くようになるはずです。
-そして、もうHL-9aの機能の中でやり残したことはなくなるので、ダミーで残してあるputIc()関数やphrCmpPutIc()関数を削除します。
----
-[1]sub_bitblt()関数の宣言の下に以下を追記
 AInt *sub_aryNew(AInt n)
 {
     AInt *p = malloc(n * sizeof (AInt));
     memset((char *) p, 0, n * sizeof (AInt));
     return p;
 }
 
 void sub_aryInit(AInt *a, AInt *ip, AInt n)
 {
     memcpy((char *) a, (char *) ip, n * sizeof (AInt));
 }
-[2]initTcSub()関数に2行追加
     var[TcSubAryNew]   = (AInt) sub_aryNew;
     var[TcSubAryInit]  = (AInt) sub_aryInit;
-[3]exprSub()関数の一部を書き換え
         } else if (phrCmp(70, "[!!**0]=", epc) && priority >= 15) {
             e1 = i;
             e0 = expr(0);
             epc = ppc1;
             i = exprSub(15);
 !           putIcX64("%R_8b_%2m0; %R_8b_%0m2; %R_8b_%1m1; %R_89_04_ca;", &var[e1], &var[e0], &var[i], 0);
         } else if (phrCmp(71, "[!!**0]", epc)) {
             e1 = i; 
             i = tmpAlloc();
             e0 = expr(0);
 !           putIcX64("%R_8b_%0m2; %R_8b_%1m1; %R_8b_04_ca; %R_89_%2m0;", &var[e1], &var[e0], &var[i], 0);
             epc = ppc1;

-[4]compile()関数の一部を書き換え
         } else if (phrCmp(21, "int !!*0[!!**2];", pc)) {
             e2 = expr(2);
 +           #if (ABI_MSW64 != 0)
 !               putIcX64("%R_8b_%1m1; %R_ff_%2m2; %R_89_%0m0;", &var[tc[wpc[0]]], &var[e2], &var[TcSubAryNew], 0);
 +           #elif (ABI_SYSV64 != 0)
 +               putIcX64("%R_8b_%1m7; %R_ff_%2m2; %R_89_%0m0;", &var[tc[wpc[0]]], &var[e2], &var[TcSubAryNew], 0);
 +           #endif
         } else if (phrCmp(22, "int !!*0[!!**2] = {", pc)) {
             e2 = expr(2);
 +           #if (ABI_MSW64 != 0)
 !               putIcX64("%R_8b_%1m1; %R_ff_%2m2; %R_89_%0m0;", &var[tc[wpc[0]]], &var[e2], &var[TcSubAryNew], 0);
 +           #elif (ABI_SYSV64 != 0)
 +               putIcX64("%R_8b_%1m7; %R_ff_%2m2; %R_89_%0m0;", &var[tc[wpc[0]]], &var[e2], &var[TcSubAryNew], 0);
 +           #endif
             j = 0;
             for (i = ppc1; i < pc1; i++) {	// コンマ以外のトークンを数える.
                 if (tc[i] == TcCrBrCls) break;
                     if (tc[i] != TcComma) {
                         j++;
                     }
                 }
                 if (i >= pc1) goto err;
                 AInt *ip = malloc(j * sizeof (AInt));
                 j = 0;
                 for (i = ppc1; tc[i] != TcCrBrCls; i++) {
                     if (tc[i] == TcCrBrCls) break;
                     if (tc[i] != TcComma) {
                     ip[j] = var[tc[i]];
                     j++;
                 }
             }
 +           #if (ABI_MSW64 != 0)
 !               putIcX64("%R_8b_%0m1; ba_%1i; 41_b8_%2i; %R_ff_%3m2;", &var[tc[wpc[0]]], (IntP) ip, (IntP) (AInt) j, &var[TcSubAryInit]);
 +           #elif (ABI_SYSV64 != 0)
 +               putIcX64("%R_8b_%0m7; be_%1i; ba_%2i; %R_ff_%3m2;", &var[tc[wpc[0]]], (IntP) ip, (IntP) (AInt) j, &var[TcSubAryInit]);
 +           #endif
             ppc1 = i + 2; // } と ; の分.

-[5]以下の記述は不要なので削除(1)
 enum { OpCpy = 0, OpCeq, OpCne, OpClt, OpCge, OpCle, OpCgt, OpAdd, OpSub, OpMul, OpDiv, OpMod, OpAnd, OpShr, 
     OpAdd1, OpNeg, OpGoto, OpJeq, OpJne, OpJlt, OpJge, OpJle, OpJgt, OpLop, OpPrint, OpTime, OpEnd,
     OpPrints, OpAryNew, OpAryInit, OpArySet, OpAryGet, OpOpnWin, OpSetPix0, OpM64s, OpRgb8, OpWait,
     OpXorShift, OpGetPix, OpFilRct0, OpPrm, OpF16Sin, OpF16Cos, OpInkey, OpDrwStr0, OpGprDec, OpBitBlt };
-[6]以下の記述は不要なので削除(2)
 void putIc(int op, IntP p0, IntP p1, IntP p2, IntP p3)  // 移行中の間だけ、以下の形で残しておく.
 {
     printf("putIc: error\n");
     exit(1);
 }
-[7]以下の記述は不要なので削除(3)
 int phrCmpPutIc(int pid, String phr, int pc, int *pi, int lenExpr, int op, int *err)  // 移行中の間だけ、以下の形で残しておく.
 {
     if (phrCmp(pid, phr, pc)) {
         printf("phrCmpPutIc: error\n");
         exit(1);
     }
     return 0;
 }
----
-以上すべての改造を終えると、プログラムは899行になります。

-これでkcube.cやinvader.cが何の問題もなく動きます。

** (2) 新出機械語の説明
-配列へ代入した場合の機械語は次のようになっています。
--以下、 a[i] = x; と書いた場合で説明します。
 putIcX64("%R_8b_%2m0; %R_8b_%0m2; %R_8b_%1m1; %R_89_04_ca;", &var[e1], &var[e0], &var[i], 0);
 
 %R_8b_%2m0   RAX = [x]; // RAXはレジスタ番号0
 %R_8b_%0m2   RDX = [a]; // RDXはレジスタ番号2
 %R_8b_%1m1   RCX = [i]; // RCXはレジスタ番号1
 %R_89_04_ca  [RDX+RCX*8] = RAX;

-配列から読み込む場合の機械語は次のようになっています。
--以下、 x = a[i]; に相当する処理の場合で説明します。
 putIcX64("%R_8b_%0m2; %R_8b_%1m1; %R_8b_04_ca; %R_89_%2m0;", &var[e1], &var[e0], &var[i], 0);
 
 %R_8b_%0m2   RDX = [a]; // RDXはレジスタ番号2
 %R_8b_%1m1   RCX = [i]; // RCXはレジスタ番号1
 %R_8b_04_8a  RAX = [RDX+RCX*8];
 %R_89_%2m0   [x] = RAX; // RAXはレジスタ番号0

** (3) ここまでのまとめ(HL-9aとHL-19aを比較する)
-ということで、とりあえずHL-9aのJITコンパイラ対応は一通りできたことになります。
-もちろん現状では最適化をサボりまくっているので、十分な速度は出ていません。しかしそれでも例外なくHL-9aよりは速くなっています。

-結局この2つの違いはそれほど大きくはなくて、主な違いはputIc()で内部コードを出力するか、それともputIcX64()でx84の機械語を出すかの違いでしかありません。

-機械語の知識がなければ自力で作るのは非常に難しいです。そこが唯一にして最大のハードルでしょう。でもこれは知っているかどうかであって、アルゴリズムが難しいとかそういうのじゃないです。

-単純にJITコンパイラ化するだけだと、最適化なしではあまり速くはなりません。しかしJITコンパイラ化しないと、速度の限界はすぐに来ます。JITコンパイラにしてしまえば、最適化を入れていくことで、どんどんスピードアップする余地ができるのです。

** 次回に続く
-次回: ''a21_txt02_10''
-次回: [[a21_txt02_10]]

*こめんと欄
#comment

トップ   編集 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS