第三世代OSASKの仮想CPUの仕様(2)
- (by K, 2013.03.09)
- (osask.netに書こうと思ったんだけどパスワードを忘れて新規ページが作れない。メモを見つけるまでこっちに書いておく)
- 今回はメモリアクセスに関する話に限定
- これは結構重要なので、たくさん書くことがある。
- 普通のCPUとはかなり異なる。
- そしてCLEとの違いが大きいのもここかもしれない。
基本的な仕組み
- アドレスレジスタは、必ずなんらかの構造体のポインタが入る(もしくはNULL)。プログラム上ではそれは一時的に void * でキャストされてそのように扱われるかもしれないけど、それとは無関係にアドレスレジスタのさす先は、何らかの構造体がなければいけない。
- ポインタのキャストは認めるが、メモリ上に展開されている構造体とマッチしないようなキャストは許さない。
- これらを言語レベルではなく、仮想機械語レベルで保証する。
- mallocの際には構造体を指定しなければならず、メモリ確保成功の場合、それはOS側にも登録される。つまりmallocは領域のサイズだけではなく、領域の構造体情報も含めて記録している。
- この構造体情報があるからこそ、ポインタのキャストが不整合になっていないかを確実に調べられる。
- 配列アクセスについてはデフォルトではバウンドチェックが毎回入る。
- 32bitのint域を8bitの char [4] でキャストしてアクセスするなどの方法は全て例外になる。それがやりたいのなら32bitのintをレジスタにロードして、シフトでしかるべき値を得るべきである。
- これによってエンディアンに依存しなくなる。
- 逆に char [] に対して、intでアクセスして4倍速にする技も使えない。
- そういう方法で高速化したいのなら「ネイティブコード混在」で好きなようにすればいい。
- 性能よりもエンディアンに依存しないことを重視するのでここは譲れない。
- アドレッシング記述はこんな感じになる。
- MOV REG12,AREG04.abc.def
- MOV AREG08,AREG07.ary[REG3+2].adr
- LEA AREG05,AREG07.ary[REG3+2]
- アドレスレジスタの後に構造体のメンバ名を書く。そしてアドレッシングの中に配列アクセスが1つまでなら含められる。そして配列の添え字については定数項・データレジスタ項の2つが許される。データレジスタに対するスケール要素はないがC言語のポインタと同様に添え字が1変わるごとにアドレスが4とか12とか増えていくようになっている。
- C言語をもっと正確に模倣するのなら AREG04->abc.def になるべきなのだが、ここは細かいことは気にせずに.で記述することにする。
- バイトコード的にはメンバ名をそのまま入れるのは非効率なので、メンバIDみたいなものを入れる。添え字がある場合は、添え字までのメンバIDと、添え字のレジスタ番号(適当な常に0の定数レジスタを指定するとこの項が省略されたとみなされる)と、添え字の定数項と、添え字以降のメンバIDが並ぶ。これらはもちろん全てhh4エンコードされる。
- mallocの際に構造体が記録されるのと同様に、スタックも構造体を記録しなければいけない。だから気軽にスタックポインタを増減させるだけではダメで、それと同時に登録情報を書き換えなければいけない。つまりオーバーヘッドは大きい。
- こういう仕様なのでお気軽にPUSH/POPするのではなくて、関数のスタックフレーム内にしかるべき領域をちゃんと用意しておいて、そこへストア・そこからロードというのが基本になる。
構造体のメンバID
仮想記憶
- (書き途中)(セグメンテーション、ページングの代わりなどの説明)
こめんと欄