HLX-002

  • (by K, 2021.09.06)

(0)

  • HLXは、「10日くらいでできる!プログラミング言語自作入門」(→a21_txt01)や、その続編で作った、HL-1~HL-22b(はりぼて言語)をベースに、Kが望む理想の言語を作るプロジェクトです。
    • 中・長期の目標はa21_hlx000に書いてあります。
  • HLX-002はその2番目のバージョンです。まあHL-24みたいなものです。

(1) 概要

  • HLX-001は、HL-9a, HL-16b, HL-22bの3つを統合し、さらに改良を加えたものです。
    • ということで「できること」はこれら3つと同じです。
  • 統合されているので、モードを切り替えて使います。
    • [codemode 0] HL-9a相当の実行モードで、JITコンパイルせずに中間コードを実行します。x86, x64以外でも(ソースからHLX-002をコンパイルすれば)動くはずです。
    • [codemode 1] HL-16b相当の実行モードで、x86の32bitの機械語をJITコンパイラで生成します。
    • [codemode 2] HL-22b相当の実行モードで、x64の64bitの機械語をJITコンパイラで生成します(MS-Windows系のABI)。
    • [codemode 3] HL-22b相当の実行モードで、x64の64bitの機械語をJITコンパイラで生成します(System V系のABI)。
  • [codedump 0] runすると普通に実行します。
  • [codedump 1] runするとプログラムの機械語を出力します。コード生成はしますが、実行はしません。変数の出現頻度も表示します。
  • [codedump 2] runするとプログラムをコンパイルしてアセンブラを出力します。実行はしません。
  • [optmode 0] 最適化の際には、「実行速度優先」という方針で行います。
  • [optmode 1] 最適化の際には、「サイズ優先」という方針で行います。
  • [Q]そもそもなぜ3つを統合する必要があるの?
    • [A]HLシリーズを拡張していくにあたって、それぞれの改造を3つのバージョンに別々に適用していくのは大変なので、まず1つに統合して、それから拡張していきたかったのです。
    • さらに統合することで、3つを別々に持つよりもコンパクトにできるのではないかと考えています。
    • あと、これ一つであれもこれもできる!っていうのが好きなんです(笑)。
  • http://k.osask.jp/files/hlx002a.zip (98.2KB)
  • http://k.osask.jp/files/hlx002b.zip (101.5KB) [最新版]
    • [内容] (サイズ・行数はhlx002b時点)
    • hlx002_x86_win.exe : 32bit版の実行ファイル(45.5KB)
    • hlx002_x64_win.exe : 64bit版の実行ファイル(60.5KB)
    • mandel.cなどのサンプルプログラム
    • sub_x86_win.obj, sub_x64_win.obj : どちらもアプリをコンパイルして実行ファイルを作る場合のみに必要なオブジェクトファイルです
    • hlx002src/
      • hlx002.c (1087行) - ソースコードから共通中間コードに変換
      • esvm.c (864行) - 共通中間コードを最適化する
      • esvm_run.c (200行) - 共通中間コードをインタプリタで実行
      • esvm_x864.c (1289行) - 共通中間コードをx86/x64の機械語に変換

(2) 特徴(なにがすごいのか) →「こんなに小さくても、結構いろいろできる!」

  • [1]処理系の小ささ
    • HL-9a, HL-16b, HL-22bの3つは以下のようになっていました。それらに対し、こんなにコンパクトにまとまっています。
      ソース行数.exeのサイズ
      HL-9a772行20.0KB
      HL-16b1287行28.5KB
      HL-22b1440行45.5KB
      (合計)(3499行)(94.0KB)
      HLX-001a2993行40.0KBアセンブラ出力ができません
      HLX-002b3440行45.5KB
    • あれ?ソース行数的には、それほどコンパクトでもないな・・・。
    • まあでも実行ファイル的には明らかにコンパクトになっています。
    • 上記3つは似たような部分が多いため、1つにまとめることで共通な部分が省かれて、このような結果になっています。


  • アプリケーションのJIT結果の違い(参考)
    (1) x86, 速度優先(2) x86, サイズ優先(3) x64_win, 速度優先(4) x64_win, サイズ優先
    grd.c116バイト120バイト169バイト174バイト
    mandel.c602バイト408バイト628バイト485バイト
    maze.c1383バイト957バイト1438バイト1161バイト
    kcube.c3360バイト2020バイト3735バイト2715バイト
    invader.c1845バイト1324バイト1926バイト1520バイト
    • これらの結果は「codemode 1」にすれば簡単に確認できます。言語処理系がx86版でもx64版でも同じ結果になります。
    • (1) → codemode 1; optmode 0;
    • (2) → codemode 1; optmode 1;
    • (3) → codemode 2; optmode 0;
    • (4) → codemode 2; optmode 1;
    • (註) grd.cではサイズ優先のほうがむしろ大きくなってしまっていますが、これは書き間違いではなく、grd.cのような極端に小さなプログラムでは、最適化方針の違いがうまく結果と連動していないというだけです。

(3-1) スクリプト言語としての使い方 [REPLモード] (おすすめ)

  • (REPLは、Read-Eval-Print-Loopの略です。)
  • hlx002をコマンドライン引数なしで起動すればこのモードになります。
  • 起動するとすぐにプロンプト「>」が出ます。
  • 適当にHLXの命令を入力すれば、実行されます。
    >print 1+2+3
    6
  • runコマンドを使えば、ファイル名を指定してHLXのプログラムを実行できます。
    >run maze.c
  • (ここで注意点ですが、HLXが使っているグラフィックライブラリがウィンドウのクローズやリサイズに対応していないので、一度何かをrunして終了して、ウィンドウが開いたままの状態で再びrunする場合は、ウィンドウサイスが同じか、より小さい場合にしか実行できません。)
  • 1行で入力することさえできれば、複雑なこともできます。
    >for (i = 1; i <= 9; i++) { for (j = 1; j <= 9; j++) { printf("%2d ", i * j); } printf("\n"); }
     1  2  3  4  5  6  7  8  9
     2  4  6  8 10 12 14 16 18
     3  6  9 12 15 18 21 24 27
     4  8 12 16 20 24 28 32 36
     5 10 15 20 25 30 35 40 45
     6 12 18 24 30 36 42 48 54
     7 14 21 28 35 42 49 56 63
     8 16 24 32 40 48 56 64 72
     9 18 27 36 45 54 63 72 81
  • 一行で書くのが難しいことをやりたいときは、適当なテキストファイルに書いて保存して、runで実行してください。

(3-2) スクリプト言語としての使い方 [ダイレクトモード]

  • これはREPLを使わずにプログラムを実行する方法です。
  • hlx002にコマンドライン引数としてファイル名を渡して起動する方法です。

(3-3) 普通のコンパイラとしての使い方(JITコンパイラではなく)

  • 例としてgrd.cから.exeを作るまでの手順はこうです。
    prompt>hlx002_x86_win -asm grd.c > grd_x86_win.nas
    prompt>nasm -f win32 -o grd_x86_win.obj grd_x86_win.nas
    prompt>gcc -Wl,-s -o grd_x86_win.exe grd_x86_win.obj sub_x86_win.obj -lgdi32
    これで「grd_x86_win.exe」ができあがります!
  • 上記のx86をx64にことごとく読み替えて、nasmにも「-f win64」すれば、x64版ができます。
  • また、hlx002_x86_winやhlx002_x64_winは、設定を変えるだけでクロスコンパイラとしても機能します。
    prompt>hlx002_x64_win -asm "-cmd:codemode 1" grd.c > grd_x86_win.nas
    prompt>hlx002_x86_win -asm "-cmd:codemode 2" grd.c > grd_x64_win.nas
  • おまけのメモ
    • 上記で使うsub_x86_win.objやsub_x64_win.objは次の方法で作りました(すでに作ったものがアーカイブに含まれています)。
    • 真似をすれば他の環境用にも作れるのではないかと思います。
    • 以下の2つのgccはそれぞれx86用とx64用のMinGWを使っているので、同じものではありません(gccはHLX-002のように1つのコンパイラで複数のアーキテクチャに対応したりはしないので)。
      prompt>gcc -m32 -Wno-unused-function -O3 -I (acl-winへのパス) -c -DAARCH_X86 -o sub_x86_win.obj sub.c
      
      prompt>gcc -m64 -Wno-unused-function -O3 -I (acl-winへのパス) -c -DAARCH_X64 -o sub_x64_win.obj sub.c

(4-1) こんなプログラムが実行できますの例(1)

  • maze.c
    • 迷路作成(穴掘り法)のプログラムです。現在のHLXでは#include文はただのコメントですので、実行に際してacl.cが必要になったりはしません。
    • 乱数で迷路を作っているので、実行するたびに違う迷路が出てきます。
      #include <acl.c>
      
      #ifdef AUSE_HLX
          regVarOpt(0, i, x, y, xx, yy, dd, d0, d1, d2, d3);	// for spd.
          regVarOpt(1, xx, yy, dd, x, y, d0, d1, d2, d3);	// for siz.
      #endif
      
      void aMain()
      {
          AWindow *w;
          int i, x, y, xx, yy, d0, d1, d2, d3, d, dd;
          w = aOpenWin(752, 496, "maze", 1);
          aFillRect0(w, 752, 496, 0, 0, 0x00ff00);
          aFillRect0(w, 16, 16, 16, 16, 0x000000);
          for (i = 0; i < 1000000; i++) {
              x = ((aXorShift32() & 0x7fffffff) % 23) * 2 + 1;
              y = ((aXorShift32() & 0x7fffffff) % 15) * 2 + 1;
              if (aGetPix(w, x * 16, y * 16) == 0x000000) {
                  for (;;) {
                      d0 = d1 = d2 = d3 = 0;
                      xx = x * 16;
                      yy = y * 16;
                      aFillRect0(w, 16, 16, xx, yy, 0x000000);
                      if (x != 45) { d0 = (aGetPix(w, xx + 16, yy) != 0) * (aGetPix(w, xx + 32, yy) != 0); }
                      if (x !=  1) { d1 = (aGetPix(w, xx - 16, yy) != 0) * (aGetPix(w, xx - 32, yy) != 0); }
                      if (y != 29) { d2 = (aGetPix(w, xx, yy + 16) != 0) * (aGetPix(w, xx, yy + 32) != 0); }
                      if (y !=  1) { d3 = (aGetPix(w, xx, yy - 16) != 0) * (aGetPix(w, xx, yy - 32) != 0); }
                      d = d0 + d1 + d2 + d3;
                      if (d == 0) break;
                      dd = (aXorShift32() & 0x7fffffff) % d;
                      if (d0) { if (dd == 0) { aFillRect0(w, 16, 16, xx + 16, yy, 0x000000); x = x + 2; } dd = dd - 1; }
                      if (d1) { if (dd == 0) { aFillRect0(w, 16, 16, xx - 16, yy, 0x000000); x = x - 2; } dd = dd - 1; }
                      if (d2) { if (dd == 0) { aFillRect0(w, 16, 16, xx, yy + 16, 0x000000); y = y + 2; } dd = dd - 1; }
                      if (d3) { if (dd == 0) { aFillRect0(w, 16, 16, xx, yy - 16, 0x000000); y = y - 2; } dd = dd - 1; }
                  }
              }
          }
          aWait(-1);
      }
      http://essen.osask.jp/download/esb20191120a.png

  • 他の実行画面紹介もあります。もし見てみたければ、とりあえず HL-9a を見てください。→ a21_txt01_9a

(4-2) こんなプログラムが実行できますの例(2)

  • calendar.c
    • hlx002bから、printf()が使えるようになりました。その記念に作りました。
#include <acl.c>

// AInt inputInt() { int i; scanf("%d", &i); return i; }

void aMain()
{
    int a[12] = { 13, 40, 33, 62, 13, 42, 63, 23, 52, 3, 32, 53 };
    int y, m, w, d, d1;
    printf("year=? "); y = inputInt();
    printf("month=? "); m = inputInt() - 1;
    if (y % 4 == 0) {
        if (y % 400 == 0) { a[1] = 41; }
        if (y % 100 != 0) { a[1] = 41; }
    }
    printf("\n         %.3s %4d\n", "JanFebMarAprMayJunJulAugSepOctNovDec" + m * 3, y);
    printf("\nSun Mon Tue Wed Thu Fri Sat\n");
    w = y;
    if (m <= 1) { w = w - 1; }
    w = (w + w / 4 - w / 100 + w / 400 + a[m] / 10) % 7;
    for (d = 0; d < w; d++) {
        printf("    ");
    }
    d1 = (a[m] % 10) + 28;
    for (d = 1; d <= d1; d++) {
        printf(" %2d ", d);
        w = (w + 1) % 7;
        if (w == 0) { printf("\n"); }
    }
    if (w > 0) { printf("\n"); }
}
  • 実行結果
    >run calendar.c
    year=? 2021
    month=? 9
    
             Sep 2021
    
    Sun Mon Tue Wed Thu Fri Sat
                  1   2   3   4
      5   6   7   8   9  10  11
     12  13  14  15  16  17  18
     19  20  21  22  23  24  25
     26  27  28  29  30

(5) バージョンアップ履歴

  • hlx001c → hlx002a :
    • 「codedump 2」(アセンブラ出力)に対応。
  • hlx002a → hlx002b :
    • printf(), inputInt()のサポートを追加。

こめんと欄


コメントお名前NameLink

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2021-09-08 (水) 06:23:22 (46d)