川合のプログラミング言語自作のためのテキスト#0007

  • (by K, 2019.05.07)

(14) TJ-20 (註:番号が一気に飛んでいるのは、過去に作った他のプログラムと混同させないためです)

  • TJ-03が高評価なので、予定を変更してちょっとした発展版を作ってみようと思います。
  • TJ-03はTJ-02と比較すると圧倒的に高速でしたが、しかしまだ簡単に速くする余地が残っています。それをここで紹介したいと思います。
  • C言語には「レジスタ変数」という機能があります。しかし最近のCコンパイラはこの機能を使わなくても最適化によって自動的に同等の速さが出ます。だから自作言語でも、レジスタ変数をサポートすれば、C言語に負けない速さがでる・・・かもしれないのです。
  • どうですか?面白そうじゃないですか?ということでやってみましょう。
    → 最初から以下の #define ECX 1 までは、TJ-03と同じなので省略
    #define ECX     1
    #define EDX     2
    #define REGVAR	 'i' // レジスタ変数の変数名(固定).
    
    int isNumber(unsigned char c) → TL-02と同じなので省略.
    
    void getNumberSub()
    {
        unsigned char *p = &txt[pc], *q;
        put32(strtol(p, (char **) &q, 0));
        pc += q - p;
    }
    
    void getNumber(int reg)  // 定数は2桁以上でもOK. レジスタに値をセットするための命令群を出力する.
    {
       unsigned char c = txt[pc], *p, *q;
       if (isNumber(c) != 0) { // 数字.
           code[qc++] = 0xb8 + reg;	// MOV reg,const.
           getNumberSub();
           return;
       }
       pc++; // 1文字の変数.
       if (c == REGVAR) {
           if (reg != EDX) {
               code[qc++] = 0x89;  // MOV reg,EDX.
               code[qc++] = reg + EDX * 8 + 0xc0;
           }
       } else {
           code[qc++] = 0x8b;	// MOV reg,[const].
           code[qc++] = reg * 8 + 0x05;
           put32((int) &var[c]);
       }
    }
    
    void sub_print(int i) → TJ-03と同じなので省略
    void sub_time() → TJ-03と同じなので省略
    
    int main(int argc, const char **argv)
    {
       → ここの部分はTJ-03と同じなので省略.
       for (;;) {
           pc0 = pc;
           if (txt[pc] == 0)	// ファイル終端.
               break;
           if (txt[pc] == REGVAR && txt[pc + 1] == '=') {	// レジスタ変数への代入.
               pc += 2;
               getNumber(EDX);
               if (txt[pc] == ';') {
               } else if (txt[pc] == '+') {	// 加算.
                   pc++;
                   if (isNumber(txt[pc]) != 0) {
                       code[qc++] = 0x81;
                       code[qc++] = 0xc0 + EDX;
                       getNumberSub();
                   } else
                       goto err; // 変数の加算はまだ作ってない.
               } else
                  	goto err; // 加算以外はまだ作ってない.
               continue;
           }
           if (txt[pc] == '\n' || txt[pc] == ' ' || txt[pc] == '\t' || txt[pc] == ';') { // 空行など.
               pc++;
           } else if (txt[pc + 1] == '=') { // 2文字目が"=".
               → ここ以降はTJ-03と同じなので省略.
  • 結局どこを改造したのかというと、getNumber()を改造したことと、レジスタ変数への代入があった場合に、特別な処理をするようにしたことだけです。
  • このTJ-20では、変数iが自動でレジスタ変数として扱われます。内部的にはEDXレジスタに割り当てられます。
  • これにより、処理が以下のように単純化されます。
    処理普通の変数の場合レジスタ変数の場合
    i=3;[2命令] MOV(EAX,3); MOV([const],EAX);[1命令] MOV(EDX,3);
    i=a;[2命令] MOV(EAX,[const]); MOV([const],EAX);[1命令] MOV(EDX,[const]);
    i=i;[2命令] MOV(EAX,[const]); MOV([const],EAX);[0命令]
    i=i+5;[4命令] MOV(EAX,[const]); MOV(ECX,5); ADD(EAX,ECX); MOV([const],EAX);[1命令] ADD(EDX,5);
  • ということで、おそらくTJ-03と比較して2倍以上は速くなったのではないかと思われます(text0008には速度比較の表があります)。
  • でもC言語の速さはこんなものじゃありません。ということで、次回ではもっと速くする方法を紹介します。

次回に続く

こめんと欄


コメントお名前NameLink

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2019-05-07 (火) 18:38:45 (41d)