「10日くらいでできる!プログラミング言語自作入門」の続編#1-9

  • (by K, 2021.05.11)

(1) HL-19

  • さてなんだかんだとJITコンパイラ対応をしているうちに、あとちょっとでmandel.cくらいなら動かせそうなところまで来ました。だから一気にやってしまいます。
  • [1]sub_関数群の宣言を以下の記述と差し替え
    clock_t t0;
    AWindow *win;
    int toExit;
    
    void sub_print(AInt i)                 { printf("%d\n", (int) i); }
    void sub_time()                        { printf("time: %.3f[sec]\n", (clock() - t0) / (double) CLOCKS_PER_SEC); }
    AInt sub_aRgb8(AInt r, AInt g, AInt b) { return aRgb8(r, g, b); }
    AInt sub_XorShift()                    { return aXorShift32(); }
    AInt sub_aGetPix(AInt x, AInt y)       { return aGetPix(win, x, y); }
    AInt sub_f16Sin(AInt x)                { return (AInt) (sin(x * (2 * 3.14159265358979323 / 65536)) * 65536); }
    AInt sub_f16Cos(AInt x)                { return (AInt) (cos(x * (2 * 3.14159265358979323 / 65536)) * 65536); }
    AInt sub_aInkey(AInt opt)              { return aInkey(win, opt); }
    void sub_prints(char *s)               { printf("%s\n", s); }
    void sub_aSetPix0(AInt x, AInt y, AInt c) { aSetPix0(win, x, y, c); }
    void sub_aFilRct0(AInt xsz, AInt ysz, AInt x0, AInt y0, AInt c) { aFillRect0(win, xsz, ysz, x0, y0, c); }
    void sub_aDrwStr0(AInt x, AInt y, AInt c, AInt b, char *s)      { aDrawStr0(win, x, y, c, b, s); }
    void sub_gprDec(AInt x, AInt y, AInt w, AInt c, AInt b, AInt i) { char s[100]; sprintf(s, "%*d", w, i); aDrawStr0(win, x, y, c, b, s); }
    
    AInt sub_OpnWin(AInt xsz, AInt ysz, char *s)
    {
        if (win != 0) {
            if (win->xsiz < xsz || win->ysiz < ysz) {
                printf("openWin error\n");
                return 1;
            }
        } else {
           win = aOpenWin(xsz, ysz, s, 0);
        }
        return 0;
    }
    
    AInt sub_aWait(AInt msec)
    {
        if (msec == -1) {
            if (win != 0) {
                aFlushAll(win);
            }
            return 1;
        }
        aWait(msec);
        return 0;
    }
    
    void sub_bitblt(AInt xsz, AInt ysz, AInt x0, AInt y0, AInt *a)
    {
        AInt32 *p32 = &win->buf[x0 + y0 * win->xsiz];
        int i, j;
        for (j = 0; j < ysz; j++) {
            for (i = 0; i < xsz; i++) {
                p32[i] = a[i];
            }
            a += xsz;
            p32 += win->xsiz;
        }
    }
    
    void initTcSub()
    {
        var[TcSubPrint]    = (AInt) sub_print;
        var[TcSubTime]     = (AInt) sub_time;
        var[TcSubRgb8]     = (AInt) sub_aRgb8;
        var[TcSubXorShift] = (AInt) sub_XorShift;
        var[TcSubGetPix]   = (AInt) sub_aGetPix;
        var[TcSubF16Sin]   = (AInt) sub_f16Sin;
        var[TcSubF16Cos]   = (AInt) sub_f16Cos;
        var[TcSubInkey]    = (AInt) sub_aInkey;
        var[TcSubPrints]   = (AInt) sub_prints;
        var[TcSubSetPix0]  = (AInt) sub_aSetPix0;
        var[TcSubFilRct0]  = (AInt) sub_aFilRct0;
        var[TcSubDrwStr0]  = (AInt) sub_aDrwStr0;
        var[TcSubGprDec]   = (AInt) sub_gprDec;
        var[TcSubOpnWin]   = (AInt) sub_OpnWin;
        var[TcSubWait]     = (AInt) sub_aWait;
        var[TcSubBitBlt]   = (AInt) sub_bitblt;
    }
  • [2]exprSub1()関数の直前のプロトタイプ宣言に、以下の行を追加
    int phrCmpPutIcX64(int pid, String phr, int pc, int *pi, int lenExpr, int sub, int *err);
  • [3]exprSub()関数内の記述を差し替え
    !   } else if (phrCmp(72, "mul64shr(!!**0, !!**1, !!**2)", epc)) {
    +       e0 = expr(0);
    +       e1 = expr(1);
    +       int e2 = expr(2);
    +       i = tmpAlloc();
    +       putIcX64("%R_8b_%2m1; %R_8b_%0m0; %R_0f_af_%1m0; %R_d3_f8; %R_89_%3m0;", &var[e0], &var[e1], &var[e2], &var[i]);
    +       tmpFree(e2);
    +       if (e2 < 0) {
    +           e0 = -1;
    +       }
    !   } else if (phrCmpPutIcX64(73, "aRgb8(!!**0, !!**1, !!**2)",             epc, &i, 3, TcSubRgb8,     &e0)) {
    !   } else if (phrCmpPutIcX64(74, "aOpenWin(!!**0, !!**1, !!***2, !!***8)", epc, 0,  3, TcSubOpnWin,   &e0)) {
    +       putIcX64("%R_85_c0; 0f_85_%0l;", &var[toExit], 0, 0, 0); // 関数の戻り値によっては終了する.
            i = Tc0;
    !   } else if (phrCmpPutIcX64(75, "aXorShift32()",                          epc, &i, 0, TcSubXorShift, &e0)) {
    !   } else if (phrCmpPutIcX64(76, "aGetPix(!!**8, !!**0, !!**1)",           epc, &i, 2, TcSubGetPix,   &e0)) {
    !   } else if (phrCmpPutIcX64(77, "ff16sin(!!**0)",                         epc, &i, 1, TcSubF16Sin,   &e0)) {
    !   } else if (phrCmpPutIcX64(78, "ff16cos(!!**0)",                         epc, &i, 1, TcSubF16Cos,   &e0)) {
    !   } else if (phrCmpPutIcX64(79, "aInkey(!!***8 , !!**0)",                 epc, &i, 1, TcSubInkey,    &e0)) {
  • [4]ifgoto()関数を以下の記述と交換
    int condCode[6] = { 0x84, 0x85, 0x8c, 0x8d, 0x8e, 0x8f }; [この行追加]
    
    void ifgoto(int i, int not, int label)
    {
        int j = wpc[i];
        if (j + 3 == wpc1[i] && TcEEq <= tc[j + 1] && tc[j + 1] <= TcGt) { // 条件式の長さが3トークンで、真ん中が比較演算子だったら.
    !       putIcX64("%R_8b_%2m0; %R_3b_%3m0; 0f_%0c_%1l;", (IntP) (AInt) condCode[(tc[j + 1] - TcEEq) ^ not], &var[label], &var[tc[j]], &var[tc[j + 2]]);
        } else {
            i = expr(i);
    !       putIcX64("%R_8b_%2m0; %R_85_c0; 0f_%0c_%1l;", (IntP) (AInt) (0x85 - not), &var[label], &var[i], 0);
            tmpFree(i);
        }
    }
  • [5]compile()関数に1行追加(1)
        tmpLabelNo = 0;
        bd = lbd = 0;
    +   toExit = tmpLabelAlloc();
        for (pc = 0; pc < pc1; ) { // コンパイル開始.
  • [6]compile()関数内の記述を差し替え(1)
             } else if (phrCmp( 5, "goto !!*0;", pc)) { // goto.
    !           putIcX64("e9_%0l;", &var[tc[wpc[0]]], 0, 0, 0);
  • [7]compile()関数内の記述を差し替え(2)
            } else if (phrCmp(12, "} else {", pc) && binf[bd] == BlkIf) {
                binf[bd + IfLabel1] = tmpLabelAlloc(); // else節の終端.
    !           putIcX64("e9_%0l;", &var[binf[bd + IfLabel1]], 0, 0, 0);
    !           defLabel(binf[bd + IfLabel0]);	// ラベルに対応するicqを記録しておく.
            } else if (phrCmp( 13, "}", pc) && binf[bd] == BlkIf) {
                if (binf[bd + IfLabel1] == 0) {
    !               defLabel(binf[bd + IfLabel0]);	// ラベルに対応するicqを記録しておく.
                } else {
    !               defLabel(binf[bd + IfLabel1]);	// ラベルに対応するicqを記録しておく.
                }
                bd -= BInfSiz;
            } else if (phrCmp(14, "for (!!***0; !!***1; !!***2) {", pc)) {	// for文
                bd += BInfSiz;
                binf[bd] = BlkFor; // ブロックのタイプ.
                binf[bd + ForLopBgn] = tmpLabelAlloc(); // ループの頭に戻る用.
                binf[bd + ForCont  ] = tmpLabelAlloc(); // continue用.
                binf[bd + ForBrk   ] = tmpLabelAlloc(); // break用.
                binf[bd + ForLbd0  ] = lbd; // 古い値を保存.
                binf[bd + ForWpc01 ] = wpc [1];
                binf[bd + ForWpc11 ] = wpc1[1];
                binf[bd + ForWpc02 ] = wpc [2];
                binf[bd + ForWpc12 ] = wpc1[2];
                lbd = bd;
                e0 = expr(0);
                if (wpc[1] < wpc1[1]) {	// !!***1に何らかの式が書いてあった.
                    ifgoto(1, IfFalse, binf[bd + ForBrk]); // 最初から条件不成立ならbreakへ.
                }
    !           defLabel(binf[bd + ForLopBgn]);	// ラベルに対応するicqを記録しておく.
            } else if (phrCmp(15, "}", pc) && binf[bd] == BlkFor) {
    !           defLabel(binf[bd + ForCont]);	// ラベルに対応するicqを記録しておく.
                i = binf[bd + ForWpc01];
                j = binf[bd + ForWpc02];
                if (i + 3 == binf[bd + ForWpc11] && j + 2 == binf[bd + ForWpc12] && tc[i] == tc[j] && tc[i + 1] == TcLt && tc[j + 1] == TcPlPlus) {
                    // !!***1が「i < ?」かつ、!!***2が「i++」だったら(変数名はiじゃなくてもいいけど、共通である必要がある).
    !               putIcX64("%R_8b_%1m0; %R_ff_c0; %R_89_%1m0; %R_3b_%2m0; 0f_8c_%0l;", &var[binf[bd + ForLopBgn]], &var[tc[i]], &var[tc[i + 2]], 0);
                } else {
                    wpc [1] = binf[bd + ForWpc01];
                    wpc1[1] = binf[bd + ForWpc11];
                    wpc [2] = binf[bd + ForWpc02];
                    wpc1[2] = binf[bd + ForWpc12];
                    e2 = expr(2);
                    if (wpc[1] < wpc1[1]) {	// !!***1に何らかの式が書いてあった.
                        ifgoto(1, 0, binf[bd + ForLopBgn]);
                    } else {
    !                   putIcX64("e9_%0l;", &var[binf[bd + ForLopBgn]], 0, 0, 0);
                    }
                }
    !           defLabel(binf[bd + ForBrk]);	// ラベルに対応するicqを記録しておく.
                lbd = binf[bd+ ForLbd0]; // 以前の値を復元.
                bd -= BInfSiz;
            } else if (phrCmp(16, "continue;", pc) && lbd > 0) {
    !           putIcX64("e9_%0l;", &var[binf[lbd + ForCont]], 0, 0, 0);
            } else if (phrCmp(17, "break;", pc) && lbd > 0) {
    !           putIcX64("e9_%0l;", &var[binf[lbd + ForBrk ]], 0, 0, 0);
  • [8]compile()関数内の記述を差し替え(3)
    !       } else if (phrCmpPutIcX64(20, "prints !!**0;", pc, 0, 1, TcSubPrints, &e0)) { // prints.
  • [9]compile()関数内の記述を差し替え(4)
    !       } else if (phrCmpPutIcX64(23, "aSetPix0(!!***8, !!**0, !!**1, !!**2);",                       pc, 0, 3, TcSubSetPix0, &e0)) {
    !       } else if (phrCmpPutIcX64(24, "aWait(!!**0);",                                                pc, 0, 1, TcSubWait,    &e0)) {
    +           putIcX64("%R_85_c0; 0f_85_%0l;", &var[toExit], 0, 0, 0); 
    !       } else if (phrCmpPutIcX64(25, "aFillRect0(!!***8, !!**0, !!**1, !!**2, !!**3, !!**4);",       pc, 0, 5, TcSubFilRct0, &e0)) {
    !       } else if (phrCmpPutIcX64(26, "aDrawStr0(!!***8, !!**0, !!**1, !!**2, !!**3, !!**4);",        pc, 0, 5, TcSubDrwStr0, &e0)) {
    !       } else if (phrCmpPutIcX64(27, "gprintDec(!!***8, !!**0, !!**1, !!**2, !!**3, !!**4, !!**5);", pc, 0, 6, TcSubGprDec,  &e0)) {
    !       } else if (phrCmpPutIcX64(28, "bitblt(!!***8, !!**0, !!**1, !!**2, !!**3, !!**4);",           pc, 0, 5, TcSubBitBlt,  &e0)) {
    !       } else if (phrCmpPutIcX64(29, "printTime();",                                                 pc, 0, 0, TcSubTime,    &e0)) { // time;と同じ(C言語っぽく書けるようにした)
  • [10]compile()関数に1行追加(2)
        if (bd > 0) {
            printf("block nesting error (bd=%d, lbd=%d, pc=%d, pc1=%d\n", bd, lbd, pc, pc1);
            return -1;
        }
    !   defLabel(toExit);
        dump1 = icq;
        putIcX64("%R_81_c4_f8_01_00_00;", 0, 0, 0, 0);
        putIcX64("58; 59; 5a; 5b; 5c; 5d; 5e; 5f; 41_58; 41_59;", 0, 0, 0, 0);
        putIcX64("41_5a; 41_5b; 41_5c; 41_5d; 41_5e; 41_5f; c3;", 0, 0, 0, 0);
        icq1 = icq;
        for (i = 0; i < jp; i++) { // ジャンプ命令の最適化.
  • [11]run()関数に3行追加
    int run(String s)
    {
        if (compile(s) < 0)
            return 1;
        if (codedump == 0) {
            void (*func)() = (void (*)()) ic;
            t0 = clock();
            func();
    +       if (win != 0) {
    +           aFlushAll(win);
    +       }
        } else {
            int i, i1 = dump1 - dump0;
            for (i = 0; i < i1; i++) {
                printf("%02x ", dump0[i]);
            }
            printf("\n(len=%d)\n", i1);
        }
        return 0;
    }

  • 以上すべての改造を終えると、プログラムは893行になります。
  • これでa21_txt01_9aで紹介した、mandel.cやmaze.cが動くようになります。
  • 特にmandel.cはすごいです。HL-9aのころと比べて3.86倍くらい高速になっています。まだJITコンパイラの最適化をやっていないのに!・・・というか、JITコンパイラ化は最適化をしなくても、これくらいの高速化ができるのが普通なのです。HL-18の結果のほうが例外なんです。
    32bit64bit
    gcc-O33.71秒3.25秒
    HL-9a25.63秒25.25秒
    HL-137.56秒
    HL-196.54秒

(2) 今回新出の機械語

0f_80_%l ~ 0f_8f_%l条件分岐命令。アセンブラではJcc命令
%R_85_c0RAXを0と比較。アセンブラではTEST命令

(3) HL-9aとHL-19を比較してみる

  • これがあれば、何が何に対応しているかわかりやすいでしょう。そしてJITコンパイラが大したことないということもわかってもらえると思います。
    単純代入:
      (HL-9a) putIc   (OpCpy,                     &var[tc[wpc[0]]], &var[tc[wpc[1]]], 0, 0);
      (HL-19) putIcX64("%R_8b_%1m0; %R_89_%0m0;", &var[tc[wpc[0]]], &var[tc[wpc[1]]], 0, 0);
    
    print命令:
      (HL-9a) phrCmpPutIc   ( 4, "print !!**0;", pc, 0, 1, OpPrint,    &e0)
      (HL-19) phrCmpPutIcX64( 4, "print !!**0;", pc, 0, 1, TcSubPrint, &e0)
    
    +1する命令:
      (HL-9a) putIc   (OpAdd1,                               &var[tc[wpc[0]]], 0, 0, 0);
      (HL-19) putIcX646("%R_8b_%0m0; %R_ff_c0; %R_89_%0m0;", &var[tc[wpc[0]]], 0, 0, 0);
    
    各種二項演算子:
      いずれも基本形は以下の通り
        (HL-9a) putIc   (op, &var[tc[wpc[0]]], &var[tc[wpc[1]]], &var[tc[wpc[3]]], 0);
        (HL-19) putIcX64(op, &var[tc[wpc[0]]], &var[tc[wpc[1]]], &var[tc[wpc[3]]], 0);
    
      演算子 (HL-9a)  (HL-19)
       +      OpAdd    "%R_8b_%1m0; %R_03_%2m0; %R_89_%0m0;"
       -      OpSub    "%R_8b_%1m0; %R_2b_%2m0; %R_89_%0m0;"
       *      OpMul    "%R_8b_%1m0; %R_0f_af_%2m0; %R_89_%0m0;"
       /      OpDiv    "%R_8b_%1m0; %R_99; %R_f7_%2m7; %R_89_%0m0;"
       %      OpMod    "%R_8b_%1m0; %R_99; %R_f7_%2m7; %R_89_%0m2;"
       &      OpAnd    "%R_8b_%1m0; %R_23_%2m0; %R_89_%0m0;"
       >>     OpShr    "%R_8b_%1m0; %R_8b_%2m1; %R_d3_f8; %R_89_%0m0;"
       ==     OpCeq    "%R_8b_%1m0; %R_3b_%2m0; 0f_94_c0; 0f_b6_c0; %R_89_%0m0;"
       !=     OpCne    "%R_8b_%1m0; %R_3b_%2m0; 0f_95_c0; 0f_b6_c0; %R_89_%0m0;"
       <      OpClt    "%R_8b_%1m0; %R_3b_%2m0; 0f_9c_c0; 0f_b6_c0; %R_89_%0m0;"
       >=     OpCge    "%R_8b_%1m0; %R_3b_%2m0; 0f_9d_c0; 0f_b6_c0; %R_89_%0m0;"
       <=     OpCle    "%R_8b_%1m0; %R_3b_%2m0; 0f_9e_c0; 0f_b6_c0; %R_89_%0m0;"
       >      OpCgt    "%R_8b_%1m0; %R_3b_%2m0; 0f_9f_c0; 0f_b6_c0; %R_89_%0m0;"
    
    ループ命令:
      (HL-9a) putIc   (OpLop,                                                      &var[tc[wpc[4]]], &var[tc[wpc[0]]], &var[tc[wpc[3]]], 0);
      (HL-19) putIcX64("%R_8b_%1m0; %R_ff_c0; %R_89_%1m0; %R_3b_%2m0; 0f_8c_%0l;", &var[tc[wpc[4]]], &var[tc[wpc[0]]], &var[tc[wpc[3]]], 0);
  • (未完成)

次回に続く

こめんと欄


コメントお名前NameLink

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2021-05-11 (火) 22:57:54 (165d)