メモ Edit

コード生成 Edit

もっとシンプルなものにしよう。

レジスタ構成 Edit

Full NameShort NameComment
Stack Register PointerSRPスタックレジスタポインタ。
スタックレジスタの先頭アドレス。
言い換えるとSR00の場所。
Stack PointerSPスタックポインタ。現在のスタック位置。
Program CounterPCプログラムカウンタ。現在実行しているバイトコードのポインタ。
Link RegisterLRリンクレジスタ。戻り先のバイトコードのポインタ。
Function RegisterFR関数レジスタ。全部で16個。FR00 - FR16。
Stack RegisterSRスタックレジスタ。最大で256個。SR00 - SRFF。
(スタックの内容を操作するので厳密にはレジスタではない気がする)

レジスタの概要 Edit

  • 1つのレジスタのサイズは8バイト。
  • 4バイトでもいいかなと思ったが,64bit環境でポインタが1レジスタで表現できなくなるため,8バイトにした。
  • 1つのレジスタはUNIONで表現。
    すべてを展開すべてを収束
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
    
     
    -
    |
    -
    |
    |
    |
    |
    |
    |
    |
    !
    !
     
    
    struct Register
    {
        union
        {
            u32 data32;
            u64 data64;
            s32 i32val;
            s64 i64val;
            f32 f32val;
            f64 f64val;
            void*  ptr; // 64bit環境だとこれは8バイト。
        };
    };
    
  • レジスタ数は暫定で64。
  • レジスタ数は動的に変更し,最大で256。それを越える必要がある関数はコンパイルエラーとする。
  • レジスタは概念的なものであり実際はスタック上からとる。
  • 計算の一時領域として使うレジスタはStackRegister(スタックレジスタ)呼び,SRを略すことが多い。
  • SRP(Stack Register Pointer)からのオフセットがレジスタ番号に相当する。
  • SR01ならSRP + 0x08のアドレスとなる。
  • 関数の先頭で関数で使用するレジスタの数 x 8バイト(レジスタのサイズ)分,スタックから確保し,その先頭アドレスをSRPに設定する。

変数とレジスタ Edit

  • ローカル変数は全てレジスタに割り当てられる。
  • スコープを考慮してレジスタの使用数はなるべく少なくて済むように最適化される。
  • 一時変数もレジスタに割り当てられる。
    すべてを展開すべてを収束
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
     14
     15
     16
     17
     18
    
    -
    |
    -
    |
    |
    -
    |
    !
    |
    -
    |
    !
    |
    |
    |
    |
    !
    !
    
    utility Hoge {
    static void func()
    {
        int a; // R01
        int b; // R02
        {
            int c; // R03
        }
        int d; // R02
        {
            int e; // R03
        }
        d = (a + b) * d; 
        // (a + b)が一時変数としてR03に格納される
        // R03 = a + b; 
        // R02 = R03 * R02;
    }
    };

戻り値とレジスタ Edit

  • 戻り値はFR00に格納される。
  • FUNCRT命令が実行されるとSR00の値はFR00にコピーされる。
    すべてを展開すべてを収束
      1
      2
      3
      4
      5
      6
      7
      8
    
     
    -
    |
    -
    |
    !
    !
     
    
    utility Hoge
    {
        static int GetOne()
        {
            return 1; // FR00 = 1
        }
    };
    

引数とレジスタ Edit

  • 引数は関数レジスタのFR01から順番にレジスタに割り当てられる。
  • 非staticなメンバ関数はFR01にthisポインタが割り当てられる。
  • 関数レジスタは16個しか用意されていないので,引数の最大数はメンバ関数で14個,非メンバ関数は15個となります。
    すべてを展開すべてを収束
      1
      2
      3
      4
      5
      6
      7
      8
      9
     10
     11
     12
     13
    
     
    -
    |
    |
    |
    |
    |
    -
    |
    |
    !
    !
     
    
    pod Vector3
    {
        float x;
        float y;
        float z;
     
        void func(float addValue)
        {
            // FR01 : this
            // FR02 : addValue
        }
    }
    

アセンブラのイメージ Edit

すべてを展開すべてを収束
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 
-
|
|
!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0: void func()
1:{
2:    int i = 3;
3:    i = i + 4;
4:}
# 命令コード
Instruction:
  LOADC   R01 0x0000
  LOADC   R02 0x0004
  ADDI32  R01 R01 R02
  RETURN
 
# ローカル変数のレジスタマッピング情報
RegisterMap:
  R01: L2 i 
 
# コンパイル時に確定する定数のテーブル
ConstantTable:
  0x0000: 3
  0x0004: 4
すべてを展開すべてを収束
  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
 
-
|
!
 
-
|
|
!
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0:int add(int a, int b)
1:{
2:    return a + b;
3:}
4:void func()
5:{
6:   int i = 3;
7:   i = add(i , 4);
8:}
 
# 命令コード
Instruction:
  add:
    FUNCIN 0x03 0x03   // FunctionIn レジスタを3つ確保。そのうち,関数レジスタの内容を先頭から3つコピー。(戻り値が組み込み型なので本当は1つでいいのだけども,それは最適化でなんとかする)
    ADDI32 R00 R01 R02 // AddInt32
    FUNCRT 0x03        // FunctionReturn レジスタを3つ解放。そのうち,R00を戻り値レジスタにコピー。
 
  func:
    FUNCIN 0x02 0x00
    LDSC32 SR00 0x0000
    LDSC32 SR01 0x0004
    LDFR   FR01 SR00 // FuncReg01 = i
    LDFR   FR02 SR01 // FuncReg02 = 4    
    CALL   0x0000    // addをコール
    LDRF   SR00 FR00 // StackReg00 = FunctReg00

命令コード Edit

  • どの命令コードも32bitで統一。
命令書式コメント
LDSRC1LDSRC4 SReg ConstantTableIndexConstantTableの値を指定したスタックレジスタにロード
LDSRC2
LDSRC4
LDSRC8
LDSRSRLDSR SReg1 SReg2SReg2の値をSReg1にロード
LDBPLDBP Reg1 BasePointerOffset(S16)BasePointerから相対アドレス分移動したアドレスをReg1にロード
LDFRLDFR Reg関数戻り値レジスタの値を指定のレジスタにロード
LDFARCLDFR FAReg ConstantTableIndexConstantTableの値を指定関数引数レジスタにロード
LDFARRLDFR FAReg Reg指定のレジスタの値を指定の関数引数レジスタにロード
LDPTRLDPTR Reg1 Reg2Reg2の値をアドレスとみなし,Reg2が指す値を取得する
LDHDLLDHDL Reg1 Reg2Reg2の値をオブジェクトハンドルとみなし,Reg2が指すオブジェクトハンドルのアドレスを取得する
CPFARCPFAR RegisterCount関数引数レジスタの値を指定した数レジスタにコピー
ADDxxxADDI32 Reg1 Reg2 Reg3Reg1 = Reg2 + Reg3
SUBxxxSUBI32 Reg1 Reg2 Reg3Reg1 = Reg2 - Reg3
MULxxxMULS32 Reg1 Reg2 Reg3Reg1 = Reg2 * Reg3
DIVxxxMULS32 Reg1 Reg2 Reg3Reg1 = Reg2 / Reg3
MODxxxMODS32 Reg1 Reg2 Reg3Reg1 = Reg2 % Reg3
PUSHREGPUSHREG PushRegisterCount
POPREGPOPREG PopRegisterCount
PUSHPUSH PushStackSize
POPPOP PopStackSize
CALLCALL SymbolTableIndex
STFRSTFRR00の値を関数戻り値レジスタに設定する
RETRET

意味解析 Edit

構文解析が終わった後,コード生成より前にやること。

  • ModuleをModuleReposに登録。
  • TypePathを解決し,型にリンクさせる
  • 関数名・変数名がだぶっていないかチェック。
  • 定数(enum,immutable)の確定
  • ユーザー定義型のサイズの決定
  • ユーザー定義型の静的・非静的メンバ変数の位置を決定
  • 自動生成関数の生成
  • 関数のレジスタ割り当ての決定
  • 関数の定数テーブルを作成
  • 関数の非値型のスタックマッピングおよびスタックサイズを決定

道のり Edit

  • 意味解析
    • まずutilityと関数だけにしぼる
    • 全てのModuleをシンボルツリーに登録
    • 全ての型をシンボルツリーに登録
    • 関数をシンボルツリーに登録。
    • 関数の中身を意味解析
      • 関数の中身のTypePathやIdentPathのシンボルを解決
      • 意味解析したStatementに置き換え。

仕様変更履歴 Edit

2010/07/29
  • prototype moduleの概念を追加。
  • classの代入演算子オーバーロードを禁止に。D言語準拠にした。
  • オブジェクトハンドルの概念をなくした。
  • isと!isを追加。セットでIdentityExpressionも追加。

    ホーム 一覧 検索 最終更新 バックアップ リンク元   ヘルプ   最終更新のRSS