HLX-001 の補足ページ#1

  • (by K, 2021.08.18)

(6) HLX-001の共通中間コード

  • 共通中間コードは、HL-9aの内部コードを真似して作りました。
  • 基本的にintの配列で、命令長は5の倍数になるようになっています。
  • 先頭が0の命令は拡張命令で、この形式のみが命令長を10以上にできます。それ以外の命令は命令長が5で固定です。
    [0][1][2][3][4]
    AEs_Op0(0)nAEs_Op0Nop(0)何もしない命令です。nは命令長で、(n+1)*5が総命令長になります。
    AEs_Op0(0)nAEs_Op0DefLb(1)lblbはラベル番号です。命令長nは1以上を指定することもできますが、普通は0にします。
    AEs_Op0(0)nAEs_Op0Jmp(2)lb無条件分岐命令です。これも通常はn=0です。
    AEs_Op0(0)nAEs_Op0Jcc(3)lb条件分岐命令です。条件は先行するCMP命令で指示します。これも通常はn=0です。
    AEs_Op0(0)nAEs_Op0Ent(4)関数宣言開始の命令です。
    AEs_Op0(0)nAEs_Op0Tet(5)関数を抜ける命令です。
    AEs_Op0(0)nAEs_Op0SysFn(6)retVarfuncPtrシステム関数呼び出し(普通の関数)。詳細後述。
    AEs_Op0(0)nAEs_Op0SysFnP(7)retVarfuncPtrシステム関数呼び出し(純関数)。詳細後述。
    AEs_OpVoid(2)var000指定した変数の値をこの先参照しない(=値が壊れてもよい)という情報を表します。
    AEs_OpSetCc(3)var000先行するCMP命令の結果に応じてvarに0か1を代入します。
    AEs_OpCpy(4)v0v100v0 = v1;
    AEs_OpNeg(5)v0v100v0 = - v1;
    AEs_OpArySet(6)0v0v1v2v0[v1] = v2;
    AEs_OpAryGet(7)v0v1v20v0 = v1[v2];
    AEs_OpCmpEq(8)0v0v10CMP(v0, v1);
    AEs_OpCmpNe(9)0v0v10CMP(v0, v1);
    AEs_OpCmpLt(10)0v0v10CMP(v0, v1);
    AEs_OpCmpGe(11)0v0v10CMP(v0, v1);
    AEs_OpCmpLe(12)0v0v10CMP(v0, v1);
    AEs_OpCmpGt(13)0v0v10CMP(v0, v1);
    AEs_OpCmpRnz(14)0000直前の関数呼び出しの戻り値が0かどうか比較します。
    AEs_OpAdd(16)v0v1v20v0 = v1 + v2;
    AEs_OpSub(17)v0v1v20v0 = v1 - v2;
    AEs_OpMul(18)v0v1v20v0 = v1 * v2;
    AEs_OpDiv(19)v0v1v20v0 = v1 / v2;
    AEs_OpMod(20)v0v1v20v0 = v1 % v2;
    AEs_OpAnd(21)v0v1v20v0 = v1 & v2;
    AEs_OpShr(22)v0v1v20v0 = v1 >> v2;
    AEs_OpM64s(23)v0v1v2v3v0 = ((AInt64) v1 * (AInt64) v2) >> v3;
  • 空欄は基本的にはゼロにします。
  • Op0以外は、[1]に演算結果をしまう変数番号が入ります。[2], [3], [4]に演算に使用する変数番号を書きます。
    • このフォーマットに統一することで最適化ルーチンを単純化できています。
  • AEs_Op0SysFn, AEs_Op0SysFnPは以下のような形式になります。
    [0]AEs_Op0(0)基本命令コード。
    [1]n命令長。結果的に1以上を指定することになります。
    [2]AEs_Op0SysFn(6), AEs_Op0SysFn(7)補助命令コード。
    [3]retVar関数の戻り値を格納する変数を指定します。0なら戻り値は捨てられます。
    [4]funcPtr呼び出したい関数の関数ポインタです。
    [5]m引数の数です。引数がなければ0でも構いません。
    [6]arg0引数の変数番号です。
    [7]arg1
    [8]arg2
    [9]arg3

(7) HLX-001のx864の内部コード

  • x864では、x86/x64の機械語を出力する前に、もっと扱いやすい形式のデータを出力しています。
    • x64ではレジスタ番号の上位1bitがREXの中に持っていかれたり、modr/mで[RSP]を指定するためにはsibバイトが必要だとか、[RBP]は指定できなくて[RBP+0]にしなきゃいけないとか、とにかく細かい例外事項が多くて面倒なのです。だからそういうのが一切ないシンプルなコード体系を作り、まずはそれを出力して最適化して、最後にx86/x64の機械語を出力するようにしています。
      00;NOP(何も出力しません)
      01;RET
      02:0_reg;PUSH reg
      02:1_reg;POP reg
      03:0_2:ptr;CALL ptr
      03:1_1:lb;JMP lb
      04_reg_rmi;MOV reg,rmi (regへのload)
      05_reg_rmi;MOV rmi,reg (regからのstore)
      06:0_ijk;特殊な命令 HL-14aでいうところの%iLjk相当
      06:1_ijk;6:0は必要に応じてEAX/RAXへのロードを行うが、6:1はそのロードを行わない
      07_i;特殊な命令 HL-14aでいうところの%iS相当
      08:0_reg_rmi;ADD reg,rmi
      08:1_reg_rmi;OR reg,rmi
      08:2_reg_rmi;ADC reg,rmi
      08:3_reg_rmi;SBB reg,rmi
      08:4_reg_rmi;AND reg,rmi
      08:5_reg_rmi;SUB reg,rmi
      08:6_reg_rmi;XOR reg,rmi
      08:7_reg_rmi;CMP reg,rmi
      09_reg_rmi;IMUL reg,rmi (reg *= rmi;)
      0a:5_rmi;IMUL rmi (RDX:RAX = RAX * rmi;)
      0a:7_rmi;IDIV rmi
      0b;CQO, CDQ
      0c:4_reg;SETE AL; MOVZX reg,AL
      0c:5_reg;SETNE AL; MOVZX reg,AL
      0c:c_reg;SETL AL; MOVZX reg,AL
      0c:d_reg;SETGE AL; MOVZX reg,AL
      0c:e_reg;SETLE AL; MOVZX reg,AL
      0c:f_reg;SETG AL; MOVZX reg,AL
      0d:4_1:lb;JE lb
      0d:5_1:lb;JNE lb
      0d:c_1:lb;JL lb
      0d:d_1:lb;JGE lb
      0d:e_1:lb;JLE lb
      0d:f_1:lb;JG lb
      0e:7_reg;SAR reg,CL
      0f:7_reg_0:i;SAR reg,i
      10:0_lb;lb: (ラベル宣言)
      11_reg;TEST reg,reg
      12:3_reg;NEG reg
      13:d_reg_rmi;SHRD rmi,reg,CL (rmiがシフトされ、rmiに結果が入る。regが押し込まれる)
      14:c_reg_rmi_0:iSHRD rmi,reg,i
      15_rmi;rmi = void;
      16_reg_rmi;LEA reg,rmi
  • rmiは、レジスタ、メモリ、定数のいずれかを表すことができます。

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