ColdFire でアセンブラ (6) - そんなに最適化するなよ [ColdFire (ColdeFire) V1]
某所で 使われていた「TRAP命令に引数を渡す」プログラムを見て、 もしかしたら危ないかも知れないと思い実験してみました。
元のコード
もともとのコードはこんなものでした。 型宣言と定数宣言は適当に追加しています。
typedef byte UB; typedef int TMO; const UB FC_TSLP_TSK = 129; const UB FC_SLP_TSK = 130; const TMO TMO_FEVR = -1; #define tslp_tsk( tmout ) __slp_tsk( (UB)FC_TSLP_TSK, (TMO)tmout ) #define slp_tsk() __slp_tsk( (UB)FC_SLP_TSK, TMO_FEVR ) void __slp_tsk( UB fc, /*機能コード*/ TMO tmout /*タイムアウト時間*/ ) { __asm { trap #0; }; }
で、この関数を呼び出すために作ったサンプルがこれです。
void main(void) { for(;;) { tslp_tsk(100); slp_tsk(); tslp_tsk(200); slp_tsk(); } /* loop forever */ /* please make sure that you never leave main */ }
二つの擬似タスクを延々と呼び出します。 コンパイルすると、__slp_tsk関数の引数fcが 使われていないと文句(Warning)を言われます。 コンパイルの結果がこれです。
; 35: for(;;) { ; 36: tslp_tsk(100); ; 0x00000000 _main: ; main: 0x00000000 0x4E40 trap #0 ; ; 37: slp_tsk(); ; 0x00000002 0x4E40 trap #0 ; ; 38: tslp_tsk(200); ; 0x00000004 0x4E40 trap #0 ; ; 39: slp_tsk(); ; 0x00000006 0x4E40 trap #0 ; ; 40: } /* loop forever */ ; 0x00000008 0x60F6 bra.s *-8 ; 0x00000000 0x0000000A 0x51FC trapf
関数がインライン展開された上に 使われない引数が省略されています。 これでは、当初の目的を果たしません。
関数ごとアセンブラで記述する
そこで、策を考えました。 関数ごとアセンブラで記述してしまいます。
void __asm __slp_tsk_1( UB fc, /*機能コード*/ TMO tmout /*タイムアウト時間*/ ) { trap #0 rts }
すると、こんどは律儀にサブルーチン呼び出しをしてくれるようになります。
; 28: { ; 29: trap #0 ; 0x00000000 ___slp_tsk_1: ; __slp_tsk_1: 0x00000000 0x4E40 trap #0 ; ; 30: rts ; 0x00000002 0x4E75 rts
; 35: for(;;) { ; 36: tslp_tsk(100); ; 0x00000000 _main: ; main: 0x00000000 0x7264 moveq #100,d1 0x00000002 0x717C0081 mvs.w #129,d0 0x00000006 0x4EBA0000 jsr ___slp_tsk_1 ; ; 37: slp_tsk(); ; 0x0000000A 0xA141 mov3q #-1,d1 0x0000000C 0x717C0082 mvs.w #130,d0 0x00000010 0x4EBA0000 jsr ___slp_tsk_1 ; ; 38: tslp_tsk(200); ; 0x00000014 0x737C00C8 mvs.w #200,d1 0x00000018 0x717C0081 mvs.w #129,d0 0x0000001C 0x4EBA0000 jsr ___slp_tsk_1 ; ; 39: slp_tsk(); ; 0x00000020 0xA141 mov3q #-1,d1 0x00000022 0x717C0082 mvs.w #130,d0 0x00000026 0x4EBA0000 jsr ___slp_tsk_1 ; ; 40: } /* loop forever */ ; 0x0000002A 0x60D4 bra.s *-42 ; 0x00000000
何とか、このままインライン関数にしてくれないかなあ。
volatileという「おまじない」
次は、魔法のおまじない「volatile」をためしてみます。
void __slp_tsk_2( volatile UB fc, /*機能コード*/ volatile TMO tmout /*タイムアウト時間*/ ) { __asm { trap #0 }; }
; 46: for(;;) { ; 0x00000000 _main: ; main: 0x00000000 0x4FEFFFEC lea -20(a7),a7 ; ; 47: tslp_tsk(100); ; 0x00000004 0x7064 moveq #100,d0 0x00000006 0x2F400008 move.l d0,8(a7) 0x0000000A 0x1EBC0081 move.b #-127,(a7) ; '.' 0x0000000E 0x4E40 trap #0 ; ; 48: slp_tsk(); ; 0x00000010 0xA16F000C mov3q #-1,12(a7) 0x00000014 0x1F7C00820001 move.b #-126,1(a7) ; '.' 0x0000001A 0x4E40 trap #0 ; ; 49: tslp_tsk(200); ; 0x0000001C 0x717C00C8 mvs.w #200,d0 0x00000020 0x2F400004 move.l d0,4(a7) 0x00000024 0x1F7C00810002 move.b #-127,2(a7) ; '.' 0x0000002A 0x4E40 trap #0 ; ; 50: slp_tsk(); ; 0x0000002C 0xA16F0010 mov3q #-1,16(a7) 0x00000030 0x1F7C00820003 move.b #-126,3(a7) ; '.' 0x00000036 0x4E40 trap #0 ; ; 51: } /* loop forever */ ; 0x00000038 0x60CA bra.s *-52 ; 0x00000004 0x0000003A 0x51FC trapf
揮発しないように、スタックに積んでくれました。 それをレジスタ渡しにして欲しいんだけど。
屋上屋を重ねる
つまり、明示的にレジスタに引数を入れればいいのだな。
void __slp_tsk_3( UB fc, /*機能コード*/ TMO tmout /*タイムアウト時間*/ ) { __asm { move fc,d0 move tmout,d1 trap #0 }; }
; 57: for(;;) { ; 0x00000000 _main: ; main: 0x00000000 0x4FEFFFEC lea -20(a7),a7 ; ; 58: tslp_tsk(100); ; 0x00000004 0x7464 moveq #100,d2 0x00000006 0x2F420008 move.l d2,8(a7) 0x0000000A 0x1EBC0081 move.b #-127,(a7) ; '.' 0x0000000E 0x2017 move.l (a7),d0 0x00000010 0x222F0008 move.l 8(a7),d1 0x00000014 0x4E40 trap #0 ; ; 59: slp_tsk(); ; 0x00000016 0xA16F000C mov3q #-1,12(a7) 0x0000001A 0x1F7C00820001 move.b #-126,1(a7) ; '.' 0x00000020 0x202F0001 move.l 1(a7),d0 0x00000024 0x222F000C move.l 12(a7),d1 0x00000028 0x4E40 trap #0 ; ; 60: tslp_tsk(200); ; 0x0000002A 0x757C00C8 mvs.w #200,d2 0x0000002E 0x2F420004 move.l d2,4(a7) 0x00000032 0x1F7C00810002 move.b #-127,2(a7) ; '.' 0x00000038 0x202F0002 move.l 2(a7),d0 0x0000003C 0x222F0004 move.l 4(a7),d1 0x00000040 0x4E40 trap #0 ; ; 61: slp_tsk(); ; 0x00000042 0xA16F0010 mov3q #-1,16(a7) 0x00000046 0x1F7C00820003 move.b #-126,3(a7) ; '.' 0x0000004C 0x202F0003 move.l 3(a7),d0 0x00000050 0x222F0010 move.l 16(a7),d1 0x00000054 0x4E40 trap #0 ; ; 62: } /* loop forever */ ; 0x00000056 0x60AC bra.s *-82 ; 0x00000004
何でわざわざスタックを使うかなあ。 しかも「move.l 1(a7),d0」ってのは、おかしいだろう。
さて、次はどんな書き方をしてやろうか。
続く…かも
ナルホド、つまりサービスコールのフロントエンドはアセンブラで書け!という事ですね。
直しとこう。
by hamayan (2008-02-19 09:20)
もし、"__slp_tsk"が別オブジェクト・ファイルに入っているのだったら、さすがにインライン展開はされないと思います。これも試しておかなくては。
だとすると、問題が出るのは、オペレーティング・システムの中味の方ですね。
とにかく、Disassemblerは手放せないようで。
by noritan (2008-02-19 14:09)
> とにかく、Disassemblerは手放せないようで
その前に、さんざん警告が出ているのに無視した私が間抜けでした。
by hamayan (2008-02-19 14:39)