* 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)|n|AEs_Op0Nop(0)|||何もしない命令です。nは命令長で、(n+1)*5が総命令長になります。|
|AEs_Op0(0)|n|AEs_Op0DefLb(1)|lb||lbはラベル番号です。命令長nは1以上を指定することもできますが、普通は0にします。|
|AEs_Op0(0)|n|AEs_Op0Jmp(2)|lb||無条件分岐命令です。これも通常はn=0です。|
|AEs_Op0(0)|n|AEs_Op0Jcc(3)|lb||条件分岐命令です。条件は先行するCMP命令で指示します。これも通常はn=0です。|
|AEs_Op0(0)|n|AEs_Op0Ent(4)|||関数宣言開始の命令です。|
|AEs_Op0(0)|n|AEs_Op0Tet(5)|||関数を抜ける命令です。|
|AEs_Op0(0)|n|AEs_Op0SysFn(6)|retVar|funcPtr|システム関数呼び出し(普通の関数)。詳細後述。|
|AEs_Op0(0)|n|AEs_Op0SysFnP(7)|retVar|funcPtr|システム関数呼び出し(純関数)。詳細後述。|
|||||||
|AEs_OpVoid(2)|var|0|0|0|指定した変数の値をこの先参照しない(=値が壊れてもよい)という情報を表します。|
|AEs_OpSetCc(3)|var|0|0|0|先行するCMP命令の結果に応じてvarに0か1を代入します。|
|AEs_OpCpy(4)|v0|v1|0|0|v0 = v1;|
|AEs_OpNeg(5)|v0|v1|0|0|v0 = - v1;|
|AEs_OpArySet(6)|0|v0|v1|v2|v0[v1] = v2;|
|AEs_OpAryGet(7)|v0|v1|v2|0|v0 = v1[v2];|
|AEs_OpCmpEq(8)|0|v0|v1|0|CMP(v0, v1);|
|AEs_OpCmpNe(9)|0|v0|v1|0|CMP(v0, v1);|
|AEs_OpCmpLt(10)|0|v0|v1|0|CMP(v0, v1);|
|AEs_OpCmpGe(11)|0|v0|v1|0|CMP(v0, v1);|
|AEs_OpCmpLe(12)|0|v0|v1|0|CMP(v0, v1);|
|AEs_OpCmpGt(13)|0|v0|v1|0|CMP(v0, v1);|
|AEs_OpCmpRnz(14)|0|0|0|0|直前の関数呼び出しの戻り値が0かどうか比較します。|
|AEs_OpAdd(16)|v0|v1|v2|0|v0 = v1 + v2;|
|AEs_OpSub(17)|v0|v1|v2|0|v0 = v1 - v2;|
|AEs_OpMul(18)|v0|v1|v2|0|v0 = v1 * v2;|
|AEs_OpDiv(19)|v0|v1|v2|0|v0 = v1 / v2;|
|AEs_OpMod(20)|v0|v1|v2|0|v0 = v1 % v2;|
|AEs_OpAnd(21)|v0|v1|v2|0|v0 = v1 & v2;|
|AEs_OpShr(22)|v0|v1|v2|0|v0 = v1 >> v2;|
|AEs_OpM64s(23)|v0|v1|v2|v3|v0 = ((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:0_rmi;|IMUL rmi (RDX:RAX = RAX * rmi;)|
|0a:1_rmi;|IDIV 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:i|SHRD rmi,reg,i|
|15_rmi;|rmi = void;|
|16_reg_rmi;|LEA reg,rmi|
-rmiは、レジスタ、メモリ、定数のいずれかを表すことができます。

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