ARM での software packed 演算による飽和加算(2)
とりあえず RGB565 の飽和加算でのレジスタの数を出来る限り少なくしてみることにします。
; r7 = 0xf7de8410 ; r8 = 0xfbdf0 ; r9 = 0x7beffbff AND r0, c0, c1 ; c0 & c1 EOR r1, c0, c1 ; c0 ^ c1 AND r1, r1, r7, LSR #16 ; r1 = (c0 ^ c1) & 0xf7de ADD r0, r0, r1, LSR #1 ; r0 = (c0 & c1) + (r1 >> 1) AND r0, r0, r7 ; c = r0 & 0x8410 ADD r0, r0, r8 ; c + 0xfbdf0 AND r0, r9, r0, LSR #5 ; r0 = (((c + 0xfbdf0)) >> 5) & 0xfbff; ADD r0, r0, #0x200 ; r0 = r0 + 0x200 EOR r0, r0, r9, LSR #16 ; r0 = r0 ^ 0x7bef ADD r1, c0, c1 ; c0 + c1 SUB r1, r1, r0 ; c0 + c1 - c ORR r0, r1, r0 ; (c0 + c1 - c) | c
定数が 16bit だったので、それらを一つにまとめ、演算しています。
これでレジスタが2つ減りました。
ただ、この software packed 演算より、
u_short AddBlend(u_short c0, u_short c1) { c = ((c0 & c1) + (((c0 ^ c1) & 0xf7de) >> 1)) & 0x8410; c = ((((c + 0x8010) >> 5) & 0x0821) + 0x7bef)^0x7bef; return (c0 + c1 - c) | c; }
この式を計算した方が、レジスタの数は減りそうです。
さっきと同じように最適化してやると、
; r7 = 0xf7de8410 ; r8 = 0x8010 ; r9 = 0x7bef0821 AND r0, c0, c1 ; c0 & c1 EOR r1, c0, c1 ; c0 ^ c1 AND r1, r1, r7, LSR #16 ; r1 = (c0 ^ c1) & 0xf7de ADD r0, r0, r1, LSR #1 ; r0 = (c0 & c1) + (r1 >> 1) AND r0, r0, r7 ; c = r0 & 0x8410 ADD r0, r0, r8 ; r0 = c + 0x8010 AND r0, r9, r0, LSR #5 ; r0 = (r0 >> 5) & 0x0821 ADD r0, r0, r9, LSR #16 ; r0 = r0 + 0x7bef EOR r0, r0, r9, LSR #16 ; r0 = r0 ^ 0x7bef ADD r1, c0, c1 ; c0 + c1 SUB r1, r1, r0 ; c0 + c1 - c ORR r0, r1, r0 ; (c0 + c1 - c) | c
このようになり、これだとレジスタの数は変わりませんが、0x8410, 0x8010, 0x0821 の値が似ていることを利用して、
0x8410 : 1000 0100 0001 0000 0x8010 : 1000 0000 0001 0000 0x0821 : 0000 1000 0010 0001
これらを一つにまとめてやります。
いろいろビットを動かして共通なビットを探してやると、
0x8010 : 1000000000010000 0x8410 : 1000010000010000 0x0821 : 0000100000100001 → 1000 0000 0001 0000 1000 0010 0001
となるので、0x08010821 というビット列が求められます。つまり、
0x8410 : 0x08010821 >> 1 0x8010 : 0x08010821 >> 12 0x0821 : 0x08010821
で、それぞれの値を求めることが出来ます。
これを使ってもう一度最適化してやると、
; r7 = 0x7beff7de ; r8 = 0x08010821 AND r0, c0, c1 ; c0 & c1 EOR r1, c0, c1 ; c0 ^ c1 AND r1, r1, r7 ; r1 = (c0 ^ c1) & 0xf7de ADD r0, r0, r1, LSR #1 ; r0 = (c0 & c1) + (r1 >> 1) AND r0, r0, r8, LSR #1 ; c = r0 & 0x8410 ADD r0, r0, r8, LSR #12 ; r0 = c + 0x8010 AND r0, r8, r0, LSR #5 ; r0 = (r0 >> 5) & 0x0821 ADD r0, r7, r0, LSL #16 ; r0 = (r0 << 16) + 0x7beff7de EOR r0, r0, r7 ; r0 = r0 ^ 0x7beff7de ADD r1, c0, c1 ; r1 = c0 + c1 SUB r1, r1, r0, LSR #16 ; r1 = r1 - (r0 >> 16) ORR r0, r1, r0, LSR #16 ; r1 | (r0 >> 16)
こうなって、更に一つレジスタが減りました。
途中で左16ビットシフトしながら計算して、最後に16ビット右シフトしていますが、これは最後の減算で、上位に変な値が残らないようにするためです。
これで、全部で6つのレジスタを使用しているわけですが、c0, c1 は RGB565 なので、これを一つのレジスタに詰めて、シフトしながら計算すれば、全部で5つのレジスタになります。
; r7 = 0x7beff7de ; r8 = 0x08010821 ; r2 = c0 | (c1 << 16) AND r0, r2, r2, LSR #16 ; r0 = r2 & (r2 >> 16) EOR r1, r2, r2, LSR #16 ; r1 = r2 ^ (r2 >> 16) AND r1, r1, r7 ; r1 = r1 & 0x7beff7de ADD r0, r0, r1, LSR #1 ; r0 = r0 + (r1 >> 1) AND r0, r0, r8, LSR #1 ; r0 = r0 & (0x08010821 >> 1) ADD r0, r0, r8, LSR #12 ; r0 = r0 + (0x08010821 >> 1) AND r0, r8, r0, LSR #5 ; r0 = (r0 >> 5) & 0x08010821 ADD r0, r7, r0, LSL #16 ; r0 = (r0 << 16) + 0x7beff7de EOR r0, r0, r7 ; r0 = r0 ^ 0x7beff7de ADD r1, r2, r2, LSR #16 ; r1 = r2 + (r2 >> 16) SUB r1, r1, r0, LSR #16 ; r1 = r1 - (r0 >> 16) ORR r0, r1, r0, LSR #16 ; r0 = r1 | (r0 >> 16)
上位16ビットにゴミが乗ってますが、ハーフワードでストアするので大丈夫でしょう。
最初が9つのレジスタを使用していたので、かなり削れましたヽ(´ー`)ノ
でもこれ、ちゃんと動くのかなぁ(;´Д`)