フィックスターズの3.7 演習問題 (3-2) 大文字変換プログラムの解答で示されたSPE用ソースコードにバグがあることを見つけた。
小文字を大文字に変換する単純なプログラムで、サンプルの文字列、"A Quick Brown Fox Jumps Over The Lazy Dog.\0"だと問題ないように見えるが、代わりに"Linux\0"を処理すると、"LINUW"と表示されてしまう。
SPE用ソースコード内の小文字を大文字に変換する部分を以下に示すが、赤字で示すコードが誤りだ。
vpata = spu_cmpgt(va, vin[i]); vpatz = spu_cmpgt(vin[i], vz); vpat = spu_or(vpata, vpatz); vout_upper = (vector unsigned char) spu_sub((vector unsigned short) vin[i], (vector unsigned short) voffset); vout[i] = spu_sel(vout_upper, vin[i], vpat);
無理やりvector unsigned charからvector unsigned shortにキャストしてvoffset(0x20)の引き算を行っているが、これだと、"Linux\0"は"Li", "nu", "x\0"となり、それぞれから"0x20, 0x20"を引くことになる。上位の桁と下位の桁で繰り下げがなければ問題がないが、繰り下げがあると上位の桁に影響が出てきてしまうのである。"Li", "nu"は繰り下げは起きないが、"x\0"の場合、"0x78, 0x00"からそれぞれ0x20を引くわけで、"0x57, 0xe0"となる。0xe0は小文字の文字コード範囲から外れているので無視されるが、0x57は"W"として認識される。よって、出力が"LINUW"となってしまうのだ。
以下のように直せばよい(赤字で示したコードが変更部分)。voffsetは型が変わっているので注意。
vector unsigned short voffset = spu_splats((unsigned short) ('a' - 'A')); vector unsigned char vzero = spu_splats((unsigned char) 0); vector unsigned char vpat1 = (vector unsigned char) { 0x10, 0x00, 0x10, 0x01, 0x10, 0x02, 0x10, 0x03, 0x10, 0x04, 0x10, 0x05, 0x10, 0x06, 0x10, 0x07 }; vector unsigned char vpat2 = (vector unsigned char) { 0x10, 0x09, 0x10, 0x0a, 0x10, 0x0b, 0x10, 0x0c, 0x10, 0x0d, 0x10, 0x0e, 0x01, 0x0e, 0x10, 0x0f }; vector unsigned char vpat3 = (vector unsigned char) { 0x01, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f }; vector unsigned short vin_s1, vin_s2, vin_s1_sub, vin_s2_sub; vpata = spu_cmpgt(va, vin[i]); vpatz = spu_cmpgt(vin[i], vz); vpat = spu_or(vpata, vpatz); vin_s1 = (vector unsigned short) spu_shuffle(vin[i], vzero, vpat1); vin_s2 = (vector unsigned short) spu_shuffle(vin[i], vzero, vpat2); vin_s1_sub = spu_sub(vin_s1, voffset); vin_s2_sub = spu_sub(vin_s2, voffset); vout_upper = (vector unsigned char) spu_shuffle(vin_s1_sub, vin_s2_sub, vpat3); vout[i] = spu_sel(vout_upper, vin[i], vpat);
もしかしたら、複雑な操作を避けるためにわざとバグ入りコードにしているかもしれないが、これで学習した人が誤って覚えてしまう危険性があるので、一言注意書きはいると思う。
小文字を大文字に変換する単純なプログラムで、サンプルの文字列、"A Quick Brown Fox Jumps Over The Lazy Dog.\0"だと問題ないように見えるが、代わりに"Linux\0"を処理すると、"LINUW"と表示されてしまう。
SPE用ソースコード内の小文字を大文字に変換する部分を以下に示すが、赤字で示すコードが誤りだ。
vpata = spu_cmpgt(va, vin[i]); vpatz = spu_cmpgt(vin[i], vz); vpat = spu_or(vpata, vpatz); vout_upper = (vector unsigned char) spu_sub((vector unsigned short) vin[i], (vector unsigned short) voffset); vout[i] = spu_sel(vout_upper, vin[i], vpat);
無理やりvector unsigned charからvector unsigned shortにキャストしてvoffset(0x20)の引き算を行っているが、これだと、"Linux\0"は"Li", "nu", "x\0"となり、それぞれから"0x20, 0x20"を引くことになる。上位の桁と下位の桁で繰り下げがなければ問題がないが、繰り下げがあると上位の桁に影響が出てきてしまうのである。"Li", "nu"は繰り下げは起きないが、"x\0"の場合、"0x78, 0x00"からそれぞれ0x20を引くわけで、"0x57, 0xe0"となる。0xe0は小文字の文字コード範囲から外れているので無視されるが、0x57は"W"として認識される。よって、出力が"LINUW"となってしまうのだ。
以下のように直せばよい(赤字で示したコードが変更部分)。voffsetは型が変わっているので注意。
vector unsigned short voffset = spu_splats((unsigned short) ('a' - 'A')); vector unsigned char vzero = spu_splats((unsigned char) 0); vector unsigned char vpat1 = (vector unsigned char) { 0x10, 0x00, 0x10, 0x01, 0x10, 0x02, 0x10, 0x03, 0x10, 0x04, 0x10, 0x05, 0x10, 0x06, 0x10, 0x07 }; vector unsigned char vpat2 = (vector unsigned char) { 0x10, 0x09, 0x10, 0x0a, 0x10, 0x0b, 0x10, 0x0c, 0x10, 0x0d, 0x10, 0x0e, 0x01, 0x0e, 0x10, 0x0f }; vector unsigned char vpat3 = (vector unsigned char) { 0x01, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f }; vector unsigned short vin_s1, vin_s2, vin_s1_sub, vin_s2_sub; vpata = spu_cmpgt(va, vin[i]); vpatz = spu_cmpgt(vin[i], vz); vpat = spu_or(vpata, vpatz); vin_s1 = (vector unsigned short) spu_shuffle(vin[i], vzero, vpat1); vin_s2 = (vector unsigned short) spu_shuffle(vin[i], vzero, vpat2); vin_s1_sub = spu_sub(vin_s1, voffset); vin_s2_sub = spu_sub(vin_s2, voffset); vout_upper = (vector unsigned char) spu_shuffle(vin_s1_sub, vin_s2_sub, vpat3); vout[i] = spu_sel(vout_upper, vin[i], vpat);
もしかしたら、複雑な操作を避けるためにわざとバグ入りコードにしているかもしれないが、これで学習した人が誤って覚えてしまう危険性があるので、一言注意書きはいると思う。
コメント