株式会社シジャム・ビーティービー
HOME > CELLプロセッサ > cell > overlay > SPU code overlays take.2

SPU code overlays take.2-CELLプロセッサ

topics

今回は、overlay をどのように実現しているか、特に linker が実行...

image

今回は、overlay をどのように実現しているか、特に linker が実行バイナ リ作成時に自動的に追加する部分を中心にソースコードとアセンブラを追ってみ ます。

使用したのは overlay のサンプルとして提供されている simple です。

  • /opt/ibm/cell-sdk/prototype/src/samples/overlay/simple

overlay 機能に必要なデータとサイズ

データは以下の 2 つの構造体の配列から構成されており、_ovly_table[partition number] には各 segment の情報が入り、_ovly_buf_table[buf] には各 region にどの segment がロードされているかを表す partition number が入っています。

struct {
        u32 vma;    // SPU local store address that the section is loaded to.
        u32 size;   // Size of the overlay in bytes.
        u32 offset; // Offset in SPE executable where the section can be found.
        u32 buf;    // One-origin index into the _ovly_buf_table.
} _ovly_table[];

struct {
        u32 mapped; // One-origin index into _ovly_table for the
                    // currently loaded overlay. 0 if none.
} _ovly_buf_table[];

また、必要となるサイズですが、

  • r: region の数
  • s: segment の数
  • f: 他の segment から呼ばれる (stub が必要な) 関数の数

とすると

  • manager: about 400 bytes
  • tables: s * 16 + r * 4 bytes
  • stubs: f * 8 + s * 8 bytes

となります。table と stub のサイズに関しては、以後の関連部分のコメン トを追加してあります。

サンプル (simple) では各配列は以下のようになっています。

 // 以下の table が segment の数 (s) 分追加 (s*16 bytes)
00001050 < _ovly_table >:
 // partition number = 0
 1050:       00 00 0a 60     // u32 vma
 1054:       00 00 00 60     // u32 size
 1058:       00 00 0b 60     // u32 offset
 105c:       00 00 00 01     // u32 buf
 // partition number = 1
 1060:       00 00 0a 60     // u32 vma
 1064:       00 00 00 60     // u32 size
 1068:       00 00 0b c0     // u32 offset
 106c:       00 00 00 01     // u32 buf

 // 以下の table が region の数 (r) 分追加 (r*4 bytes)
 // region 毎に現在有効になっている segment index を保存
00001070 < _ovly_buf_table >:
 // buf (region index) = 1
 1070:       00 00 00 00

overlay 関数 (stub 関数) の呼び出し

main() 内から overlay 関数 (o1_test1(), o1_test2(), o2_test1(), o2_test2()) を呼び出していますが、呼び出しが各関数に対応した stub の呼び 出しに変わっています。このように stub 関数を介在させて、stub 関数内で overlay による code 追加を行うことで text section を変更しないで済みます。

00000300 < main >:

 300:   il      $3,101             # 65 // o1_test1(101) に対応する
 310:   brsl    $0,90 <_start+0x60># 90 // stub 関数の呼び出し
             :
 340:   il      $3,102             # 66 // o1_test2(102) に対応する
 344:   brsl    $0,90 <_start+0x60># 90 // stub 関数の呼び出し
             :        
 368:   il      $3,201             # c9 // o2_test1(201) に対応する
 36c:   brsl    $0,b0 <_start+0x80># b0 // stub 関数の呼び出し
             :
 390:   il      $3,202             # ca // o2_test2(202) に対応する
 394:   brsl    $0,a8 <_start+0x78># a8 // stub 関数の呼び出し
             :

stub 関数

start: の後ろに overlay 関数用の stub があります。この stub では、 overlay 処理を行う manager (__ovly_load) への引数 (overlay 関数の address と partition number) をレジスタに代入して manager に分岐します。

00000030 < _start >:
             :
  88:   brsl    $0,300 
# 300 // main() 呼び出し 8c: brsl $0,418 # 418 // 終了処理 // 以下のいずれかの stub が 関数の数 (f) 分追加 (f*8 bytes) 90: ila $79,2656 # a60 // o1_test1() のアドレス 94: br a0 <_start+0x70># a0 98: ila $79,2704 # a90 // o1_test2() のアドレス 9c: nop $0 // 以下の stub が segment の数 (s) 分追加 (s*8 bytes) a0: ila $78,1 // o1_*() の partition number a4: br f0 <__ovly_load># f0 // manager に分岐 a8: ila $79,2656 # a60 // o2_test1() のアドレス ac: br b8 <_start+0x88># b8 b0: ila $79,2704 # a90 // o2_test2() のアドレス b4: nop $0 b8: ila $78,2 // o2_*() の partition number bc: br f0 <__ovly_load># f0 00000a60 < o1_test1 >: 00000a90 < o1_test2 >: 00000a60 < o2_test1 >: 00000a90 < o2_test2 >:

manager (__ovly_load) 関数

ソースコードは spu_ovl.S (rev 1.1) です。開発ツリーのソースと spu toolchain のソースは License が異なる程度 で違いはありません。大まかな処理の流れは以下のようになります。

  1. 引数として、以下を受け取り。
    • $78: overlay 関数の partition number
    • $79: overlay 関数の address
  2. スタックの back chain を遡って、overlay 関数からの呼び出しであるかを 探索。lr には、対応する関数が overlay 関数である場合、
    • return address (通常はこれのみ)
    • 一つ前の overlay 関数の address
    • 一つ前の overlay 関数の partition number
    • partition number
    が入っており、lr に格納されている return address がoverlay 関数に共通の address (__ovly_return) かどうかで判定。
  3. 上記のフォーマットで呼び出された関数用の lr を作成。
  4. partition number が 0 の場合は overlay 関数の呼び出しではないた め、__ovly_load_restore へ分岐して復帰処理。
  5. _ovly_table[partition number] から overlay 領域の情報を取得。
  6. _ovly_buf_table[buf] と partiton number を比較して、すでに領域がロー ドされているかを判定し、ロードされていない場合は DMA 転送。
  7. overlay 関数の呼び出し。
  8. overlay 関数から戻ってきたら
    • $78: 一つ前の overlay 関数の partition number
    • $79: 一つ前の overlay 関数の address
    として 1. から再実行。

このように一つ前の overlay 関数の情報を記録しておいて、overlay 関数から 戻ってきた後に、一つ前の overlay 関数の呼び出しであるかのように manager(__ovly_load) を再実行することで、前回説明した overlay region 間を ピンポンする関数呼び出しのような自由度の高い関数呼び出しを可能にしていま す。

_ovly_table[], _ovly_buf_table[] の作成

処理しているソースは binutils/bfd/ の cpu-spu.c, bfd/elf32-spu.c です。ソースも若干異なるようなのですが、 linker(ld, bfd) がどのような処理をしているのかさっぱりわからないので今回 はパスで、、

トラックバック

トラックバックURL:

コメントを投稿


※上に表示されている番号を入力してください