* Shiba VM [#y3d91a37]
#contents
シンプルでもいける気がしてきた。

** レジスタの概要 [#b2e56c51]
- 1つのレジスタのサイズは8バイト。
- 4バイトでもいいかなと思ったが,64bit環境でポインタが1レジスタで表現できなくなるため,8バイトにした。
- ただし,メモリがきつきつの環境もあるので32bitモード用意する。その場合,64bitの整数・浮動小数,64bitのアドレス空間は扱えなくなる。
- 1つのレジスタはUNIONで表現。
#code(c,){{
struct Register
{
    union
    {
        u32 data32;
        u64 data64;
        s32 i32val;
        s64 i64val;
        f32 f32val;
        f64 f64val;
        void*  ptr; // 64bit環境だとこれは8バイト。
    };
};

}}

** レジスタ構成 [#ka053590]
|Full Name|Short Name|Comment|h
|Stack Register Pointer|SRP|スタックレジスタポインタ。&br; スタックレジスタの先頭アドレス。 &br;言い換えるとSR00の場所。|
|Stack Pointer|SP|スタックポインタ。現在のスタック位置。|
|Program Counter|PC|プログラムカウンタ。現在実行しているバイトコードのポインタ。|
|Program Function|PF|プログラムの関数オブジェクトのポインタ。デバッグ用。|
|Program Line|PL|プログラムのソースコードの行数。デバッグ用。|
|Link Register|LR|リンクレジスタ。戻り先のバイトコードのポインタ。|
|Function Register|FR|関数レジスタ。全部で16個。FR00 - FR16。|

#code(c,){{
struct Context
{
   // 関数開始時に保存されるレジスタ達。
   void* srp;
   void* lr;
   void* pf;
   void* pl;
   // 保存されないレジスタ達。
   void* sp;
   void* pc;
};
}}

** スタックレジスタ [#bb1584f7]
- 計算の一時領域として使うレジスタはStackRegister(スタックレジスタ)呼び,SRと略す。
- スタックレジスタ数は動的に変更し,最大で256。それを越える必要がある関数は''コンパイルエラー''とする。
- SRP(Stack Register Pointer)からのオフセットがスタックレジスタの番号に相当する。
- SR01ならSRP + 0x08のアドレスとなる。(32bitモードなら+0x04)
- 通常,関数の先頭でFUNCET命令を使い関数で使用するスタックレジスタの数 x レジスタのサイズ分,スタックから確保し,その先頭アドレスをSRPに設定する。
** 変数とレジスタ [#w8f36726]
- ローカル変数は全てスタックレジスタに割り当てられる。
- スコープを考慮してスタックレジスタの使用数はなるべく少なくて済むように最適化される。
- 一時変数もスタックレジスタに割り当てられる。
#code(c,){{
utility Hoge {
static void func()
{
    int a; // SR01
    int b; // SR02
    {
        int c; // SR03
    }
    int d; // SR02
    {
        int e; // SR03
    }
    d = (a + b) * d; 
    // (a + b)が一時変数としてSR03に格納される
    // SR03 = a + b; 
    // SR02 = R03 * R02;
}
};
}}
** 戻り値とレジスタ [#lb8a56e2]
- 戻り値はFR00に格納される。
- FUNCRT命令が実行されるとSR00の値はFR00にコピーされる。
#code(c,){{
utility Hoge
{
    static int GetOne()
    {
        return 1; // FR00 = 1
    }
};

}}

** 引数とレジスタ [#g01df345]
- 引数は関数レジスタのFR01から順番にレジスタに割り当てられる。
- 非staticなメンバ関数はFR01にthisポインタが割り当てられる。
- 関数レジスタは16個しか用意されていないので,引数の最大数はメンバ関数で14個,非メンバ関数は15個となります。(FR00は戻り値として使うため,引数に使用できない)
#code(c,){{
pod Vector3
{
    float x;
    float y;
    float z;

    void func(float addValue)
    {
        // FR01 : this
        // FR02 : addValue
    }
}

}}
** アセンブラのイメージ [#sc9a365f]
- 簡単なコード
#code(c,){{
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:
    DFUNC  0x0000
    DLINE  1 // '{'の位置
    FUNCET 0x03 0x03  
    DLINE  2
    ADDI32 R00 R01 R02
    DLINE  3 // '}'の位置
    FUNCLV 0x03       

  func:
    DFUNC   0x0001
    DLINE   5
    FUNCET  0x02 0x00
    DLINE   6
    LDSRC4  SR00 0x0000
    DLINE   7
    LDSRC4  SR01 0x0004
    LDFRSR  FR01 SR00 
    LDFRSR  FR02 SR01
    CALL    0x0000   
    LDRF    SR00 FR00
    FUNCLV  0x02

# コンパイル時に確定する定数のテーブル
ConstantTable:
  0x0000: 3
  0x0004: 4

SymbolTable:
  0x0000: int add(int,int)
  0x0004: void func()

}}
** 命令コード [#ac30d5cb]
- どの命令コードも32bitで統一。
- SRegおよびFRegはu8。

|命令|書式|コメント|h
|>|>|BGCOLOR(ORANGE):''ロード''|
|LDSRC1|LDSRC4 SReg ConstantTableIndex(u16)|ConstantTableの値を指定したスタックレジスタにロード|
|LDSRC2|~|~|
|LDSRC4|~|~|
|LDSRC8|~|~|
|LDSRSR|LDSRSR SReg1 SReg2|SReg2の値をSReg1にロード|
|LDSRSP|LDSRSP SReg1 StackPointerOffset(u16)|StackPointerから相対アドレス分移動したアドレスをReg1にロード|
|LDSRP1|LDSRP1 SReg1 SReg2|SReg2の値をアドレスとみなし,SReg2が指す値を取得する|
|LDSRP2|~|~|
|LDSRP4|~|~|
|LDSRP8|~|~|
|LDSROJ|LDSROJ SReg1 SReg2|SReg2の値をobject型とみなし,SReg2が指すobjectの実体のアドレスのアドレスを取得する|
|LDFRSR|LDFRSR FReg SReg|SRegの値をFRegにロード|
|>|>|BGCOLOR(ORANGE):''演算''|
|ADDxxx|ADDI32 SReg1 SReg2 SReg3|SReg1 = SReg2 + SReg3|
|SUBxxx|SUBI32 SReg1 SReg2 SReg3|SReg1 = SReg2 - SReg3|
|MULxxx|MULS32 SReg1 SReg2 SReg3|SReg1 = SReg2 * SReg3|
|DIVxxx|MULS32 SReg1 SReg2 SReg3|SReg1 = SReg2 / SReg3|
|MODxxx|MODS32 SReg1 SReg2 SReg3|SReg1 = SReg2 % SReg3|
|>|>|BGCOLOR(ORANGE):''関数あれこれ''|
|FENTER|FENTER AllocSRegNum CopyFRegNum|Function Enter. &br;まずスタックに現在のSPR,LR,PL,PFをつみ,SPを変更する。&br;AllocSRegNumで指定された数,スタックレジスタを確保し,SPRに設定する。SPを変更する。&br;CopyFRegNumで指定された数,スタックレジスタにFRに内容をコピーする。|
|FLEAVE|FLEAVE FreeSRegNum|Function Leave. &br;FreeSRegNumで指定された数,スタックレジスタを解放しSPを変更する。&br;PF,PL,LR,SPR,をスタックから取り出し設定する。SPも変更する。&br;PCにLRを代入する。|
|CALL|CALL SymbolTableIndex(u16)|LRにPCを代入する。 &br;SymbolTableIndexが示す関数の先頭バイトコードをPCに設定する。|
|CALLVF|CALLVF SReg1 ConstantTableIndex(u16)|Call Virtual Function.&br;SReg1が指すオブジェクトの仮想関数の先頭バイトコードを求め,PCに代入する。&br;ConstantTableIndexが示す先にはSymbolTableIndex(u16)が2つ連続して存在する。&br;1つめは仮想関数の親となるSSObjectTypeが格納されているSymbolTableのIndex。&br;2つめは仮想関数自体のSSFunctionが格納されているSymbolTableのIndex。|
|>|>|BGCOLOR(ORANGE):''スタック''|
|PUSH|PUSH SReg ConstantTableIndex(u16)|ConstantTableIndexが指す定数サイズ(u32)減算し,指定のSRegに演算後のSPを設定する。|
|POP|POP ConstantTableIndex(u16)|ConstantTableIndexが指す定数サイズ(u32)分,SPを加算する。|
|>|>|BGCOLOR(ORANGE):''オブジェクト''|
|OBJNEW|OBJNEW SReg SymbolIndex|Object New.&br;指定のSymbolIndexが指すObjectTypeのインスタンスを作成し,ハンドルをSReg1に代入する。|
|OBJDEL|OBJDEL SReg|Object Delete.&br;指定のSRegが指すオブジェクトに対してdeleteする。|
|OBJRFI|OBJRFI SReg|Object Reference Increment.&br;指定のSRegが指すオブジェクトのリファレンスカウンタをインクリメントする。|
|OBJRFD|OBJRFD SReg|Object Reference Decrement.&br;指定のSRegが指すオブジェクトのリファレンスカウンタをデクリメントし,SRegに0を代入する。|
|>|>|BGCOLOR(ORANGE):''デバッグ''|
|DLINE|DLINE ConstantValue(u24)|Debug Line.&br;デバッグ情報のプログラム行数を代入するための命令。 &br;PLにConstantValueを代入する。|
|DFUNC|DFUNC SymbolTableIndex(u16)|Debug Function.&br;デバッグ情報の関数オブジェクトを代入するための命令。&br;PFにSymbolTableIndexが示すアドレスを代入する。|

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