libc 解析 take.2 (非可変引数関数)-CELLプロセッサ
"PPE Serviced SPE C Library Functions" ...
"PPE Serviced SPE C Library Functions" の 2 回目は、SPE 側での処理に ついて解説します。
4.2 PPE Serviced SPE C Library Functions
- The SPE constructs a local store image of the input and outout parameters.
- The SPE creates a 32-bit message consisting of an opcode and pointer to the local store parameter image array.
- The SPE executes a Stop and Signal instruction.
- The PPE detects the Stop and Signal.
- The PPE invoke a specialist to service the request according to the message opcode.
- The PPE services the requested function.
- The PPE returns results in the local store image array.
- The PPE resumes SPE execution.
- The SPE returns that results back to the caller.
この内、SPE で行うのは 1., 2., 3. ですが、呼び出す関数の引数の数が決 まっている場合 (非可変引数関数) と、可変の場合 (可変引数関数) の場合で処 理方法が変わります。
ソースコード
ソースコードは /opt/IBM/cell-sdk-1.1/src/lib/c/spu/ 以下にあるのです が、ほとんどのコードは mk_syscalls と syscall.def からコンパイル時に生成 されています。syscall.def の一部を抜き出してみると
# # CLASS OPCODE NAME PARMS RETVAL SYSCALL_TYPE # SPE_C99_CLASS SPE_C99_GETC getc 1 1 spu_syscall SPE_C99_CLASS SPE_C99_VFPRINTF fprintf 2 1 spu_syscall_va
のように各関数の属性が書かれており、mk_syscall がこの属性情報をもとに 各関数用のソースファイルを生成します。で、SYSCALL_TYPE が spu_syscall の 場合は非可変引数関数、spu_syscall_va の場合は可変引数関数となります。
非可変引数関数の場合
以下は getc() を例に説明します。まずは、生成されたソースコード (getc.S) です。
#include "spe_c99_ops.h" #include "spe_posix1_ops.h" #define OPCLASS SPE_C99_CLASS // C99 の関数 #define NAME gets // 関数名 #define PARMS 1 // 引数の個数 #define OPCODE SPE_C99_GETS // 関数の識別子 (13) #define RETVAL 1 // 返り値がある場合 #include "spu_syscall.S"
このように非可変引数関数の場合は spu_syscall.S をインクルードしていま す。次はオブジェクトファイルの逆アセ結果で説明をコメントとして追加してあ ります。
注意すべき点としては、stack_instr: に書かれた命令列は一旦レジスタにロー ドされ、加工された後にスタック上にセーブし、最終的にはスタック上の命令が 実行されるところです。
getc.o: file format elf32-spu Disassembly of section .text: 00000000: 0: 41 09 80 02 ilhu $2,4864 // OPCODE(13) を $2 にロード 4: 24 ff 40 83 stqd $3,-48($1) // 引数 ($3) を -48(sp) にセーブ 8: 33 80 07 06 lqr $6,40 // stack_instr: の命令列を $6 にロード c: 1c f4 00 83 ai $3,$1,-48 // 引数をセーブするアドレスを $3 にロード 10: 43 ff ff 84 ila $4,3ffff // マスク値を生成 14: 80 40 c1 04 selb $2,$2,$3,$4 // [24:31] OPCODE, [0:17] 引数のアドレス 18: 3e c1 00 85 cwd $5,4($1) // マスク値を生成 1c: b0 41 81 05 shufb $2,$2,$6,$5 // $2 を命令列 2 word 目に挿入 20: 1c f8 00 83 ai $3,$1,-32 // 命令列をセーブするアドレスをロード 24: 24 ff 80 82 stqd $2,-32($1) // 命令列をスタックにセーブ 28: 00 40 00 00 sync 2c: 35 20 01 82 bisl $2,$3 // スタック上の命令列にジャンプ 30: 34 ff 40 83 lqd $3,-48($1) // 返り値を $3 にロード 34: 3f e3 01 82 shlqbyi $2,$3,12 38: 23 80 00 02 stqr $2,0 // errno をセーブ 3c: 35 00 00 00 bi $0 // 呼び出し関数に復帰 00000040 : 40: 00 00 21 00 stop // stop OPCLASS 44: 00 00 00 00 stop // ここに OPCODE + 引数のアドレス が入る 48: 35 00 01 00 bi $2 // 30: にジャンプ 4c: 00 20 00 00 lnop
よって、スタック上の stop OPCLASS 命令を発行して SPE が停止した時のス タックはこのようになります。
| Stack | high memory
| Parms |
| |
|------------|
| Link Reg |
|------------|
| Back Chain | <---- input SP
|------------|
| |
|------------|
| 命令列 | <---- $3
|------------|
| 引数 | <---- start of parameters
`------------'
low memory
ちなみに引数が 2 つの場合はスタックフレームはこんな感じです。
| Stack | high memory
| Parms |
| |
|------------|
| Link Reg |
|------------|
| Back Chain | <---- input SP
|------------|
| |
|------------|
| 命令列 | <---- $3
|------------|
| 引数 2 |
|------------|
| 引数 1 | <---- start of parameters
`------------'
low memory
ちょっと謎なのが stack_instr 以下のコードをスタック上にセーブしてから 実行しているところです。spu_syscall.S のコメントを見ると "リエントラント にするためだ" と書いてあり、たしかに OPCLASS と引数のアドレスを書くんで、 1 SPE 内に複数のスレッドがいて、スレッド切り替え時に LS の内容が変更され ないなら必要ですが、現在のようにスレッドが切り替わる場合は LS の内容も全 部入れ換えている場合は必要ないかな、、と。まあ、今後の備え?




コメント
Woah nelly, how about them appels!
Posted by: Wood | 2011年07月20日 02:25
F5N0gm csjrnaaxvfpk
Posted by: hpaavceyvk | 2011年07月20日 22:09
WPVCEd lougzzfclabk
Posted by: xhthmaoeum | 2011年07月22日 20:56