平成26年4月 11日
最適化の悪影響
モニタプログラムの細かな変更をしてROMを書き換えた所、動かなくなってしまいました。同時に何箇所かを変更してしまったのと、その間にほかの作業が入ったために何処を変更したのか記憶に頼る結果となってしまい、少し手間取りました。結果、原因はコンパイラの最適化が悪さしていました。当初は最適化を無効にしていたのを、他のパラメータを変更したときに一緒に最適化するように変更したようです。
直接の原因はCプログラムの中にアセンブラで書いた部分の前提が最適化によって変更されています。
元のプログラムを示します。
(平26年4月12日追記)
問題になる関数はinp(), outp()の二つの関数ですが、ここではoutp()関数のみを示します。変更するときは両方の関数を変更する必要があります。
(追記ここまで)
void outp(uint addr, uchar data)
{
//引数は右側の引数から順にスタックに積まれる。
//一番左側の引数のアドレスは(sp+2)になる(スタックフレームなしの場合)
#asm
ld IY, 0
add IY,SP
ld c,(IY+2)
ld b,(IY+3)
ld a,(IY+4)
out (c),a
#endasm
}
コメントに書いてあるように、この記述では関数がスタックフレームを使用しないことを前提に関数の引数へアクセスしています。スタックフレームを使用する場合は、(IX+4)のアドレスが一番左の引数アドレスになります。最適化しないときにはスタックフレームは使用されていなかったので、上記のように書いたのですが最適化を有効にするとスタックフレームが使用されてしまいます。どのような基準でスタックフレームの要否を決めているのかは不明です。
こちらが最適化無効の時のリスト出力です。
0165 ; addr -> offset 4 unsigned int
0166 ; value -> offset 6 char
0167 %%LINE 000036
0168 %%FUNC outp,3,cls=2,type=0
0169 _outp:
0170 %%DFP 2
0171 %%SYM addr,14,type=6,cls=6,val=4,size=2
0172 %%SYM data,14,type=2,cls=6,val=6,size=1
0173 %%ENDD
0174 %%BF
00000000 00000017 FD210000 0175 ld IY, 0
0000001B FD39 0176 add IY,SP
0000001D FD4E02 0177 ld c,(IY+2)
00000020 FD4603 0178 ld b,(IY+3)
00000023 FD7E04 0179 ld a,(IY+4)
00000026 ED79 0180 out (c),a
0181 %%LINE 000047
0182 %%EF
00000028 C9 0183 ret
0184 %%FEND
次は最適化を有効にしたときのリスト出力です。青色で示す部分が追加されています。この追加部分がスタックフレームです。
0163 ; addr -> offset 4 unsigned int
0164 ; value -> offset 6 char
0165 %%LINE 000036
0166 %%FUNC outp,3,cls=2,type=0
0167 _outp:
00000000 00000012 CD0000 E 0168 call cssent0
0169 %%DFP 2
0170 %%SYM addr,14,type=6,cls=6,val=4,size=2
0171 %%SYM data,14,type=2,cls=6,val=6,size=1
0172 %%ENDD
0173 %%BF
0174 %%LINE 000047
00000000 00000015 FD210000 0175 ld IY, 0
00000019 FD39 0176 add IY,SP
0000001B FD4E02 0177 ld c,(IY+2)
0000001E FD4603 0178 ld b,(IY+3)
00000021 FD7E04 0179 ld a,(IY+4)
00000024 ED79 0180 out (c),a
0181 %%EF
00000026 DDF9 0182 ld SP,IX
00000028 DDE1 0183 pop IX
0000002A C9 0184 ret
0185 %%FEND
次は最適化を有効にしたときのリスト出力です。6行目に”call cssent0”が追加されています。この追加部分がスタックフレームです。
0163 ; addr -> offset 4 unsigned int
0164 ; value -> offset 6 char
0165 %%LINE 000036
0166 %%FUNC outp,3,cls=2,type=0
0167 _outp:
00000000 00000012 CD0000 E 0168 call cssent0
0169 %%DFP 2
0170 %%SYM addr,14,type=6,cls=6,val=4,size=2
0171 %%SYM data,14,type=2,cls=6,val=6,size=1
0172 %%ENDD
0173 %%BF
0174 %%LINE 000047
00000000 00000015 FD210000 0175 ld IY, 0
00000019 FD39 0176 add IY,SP
0000001B FD4E02 0177 ld c,(IY+2)
0000001E FD4603 0178 ld b,(IY+3)
00000021 FD7E04 0179 ld a,(IY+4)
00000024 ED79 0180 out (c),a
0181 %%EF
00000026 DDF9 0182 ld SP,IX
00000028 DDE1 0183 pop IX
0000002A C9 0184 ret
0185 %%FEND
このコンパイラにはオプションとして”スタックフレームを強制的に生成させる”というチェックボックスもあるので、これにチェックを入れる手もあるのですが、時間がたった後で今度は最適化を禁止して同じような状況にはまりかねないので止めにします。この手の小さな処理ならアセンブラで書いても知れています。ソースファイルが一つ増えますが別ファイルにしておきます。プログラムは以下になります。
misc.xasのリスト
global _inp, _outp
C_inp SECT CODE
_inp:
ld IY, 0
add IY,SP
ld c,(IY+2)
ld b,(IY+3)
in a,(c)
ld c,a ;8ビットの返り値はCレジスタで返す
ret
_outp:
ld IY, 0
add IY,SP
ld c,(IY+2)
ld b,(IY+3)
ld a,(IY+4)
out (c),a
ret
;
END