mc.c 函数:frame_init_lowres_core
C源码:
static void frame_init_lowres_core( uint8_t *src0, uint8_t *dst0, uint8_t *dsth, uint8_t *dstv, uint8_t *dstc,
int src_stride, int dst_stride, int width, int height )
{
for( int y = 0; y < height; y++ )
{
uint8_t *src1 = src0+src_stride;
uint8_t *src2 = src1+src_stride;
for( int x = 0; x<width; x++ )
{
// slower than naive bilinear, but matches asm
#define FILTER(a,b,c,d) ((((a+b+1)>>1)+((c+d+1)>>1)+1)>>1)
dst0[x] = FILTER(src0[2*x ], src1[2*x ], src0[2*x+1], src1[2*x+1]);
dsth[x] = FILTER(src0[2*x+1], src1[2*x+1], src0[2*x+2], src1[2*x+2]);
dstv[x] = FILTER(src1[2*x ], src2[2*x ], src1[2*x+1], src2[2*x+1]);
dstc[x] = FILTER(src1[2*x+1], src2[2*x+1], src1[2*x+2], src2[2*x+2]);
#undef FILTER
}
src0 += src_stride*2;
dst0 += dst_stride;
dsth += dst_stride;
dstv += dst_stride;
dstc += dst_stride;
}
}
SR-add开启后 汇编:
889 .L.482__4:
890 ldr w10, [x29,#20]
891 cmp w10, #0
892 ble .L.482__1
893 mov w0, #0
894 ldr w10, [x29,#20]
895 lsl w2, w10, #1
896 ldr w10, [x29,#24]
897 add x5, x20, w10, SXTW // x5 = src1 = scr0 + src_stride
898 mov x7, x23 <==== "dst0 + x"
899 mov x8, x24 <==== "dsth + x"
900 mov x9, x25 <==== "dstv + x"
901 mov x11, x26 <==== "dstc + x"
902 ldr w10, [x29,#24]
903 add x6, x5, w10, SXTW // x6 = src2 + 2x
904 mov x10, x20 <===== "src0 + 2x"
905 mov x1, x5 <===== "src1 + 2x"
906 add x18, x5, w2, SXTW // x18 = src1 + width * 2
907 add x2, x20, #1 <===== "src0 + 2x + 1"
908 add x12, x20, #2 <===== "src0 + 2x + 2"
909 add x3, x5, #1 <===== "src1 + 2x + 1"
910 add x13, x5, #2 <===== "src1 + 2x + 2"
911 add x4, x6, #1 <===== "src2 + 2x + 1"
912 add x14, x6, #2 <===== "src2 + 2x + 2"
913 .L.482__2:
914 ldrb w15, [x10]
915 ldrb w27, [x1]
916 add w15, w15, w27
917 add w15, w15, #1
918 asr w15, w15, #1
919 ldrb w27, [x2]
920 ldrb w28, [x3]
921 add w27, w27, w28
922 add w27, w27, #1
923 asr w27, w27, #1
924 add w15, w15, w27
925 add w15, w15, #1
926 asr w15, w15, #1
927 strb w15, [x7]
928 ldrb w15, [x2]
929 ldrb w27, [x3]
930 add w15, w15, w27
931 add w15, w15, #1
932 asr w15, w15, #1
933 ldrb w27, [x12]
934 ldrb w28, [x13]
935 add w27, w27, w28
936 add w27, w27, #1
937 asr w27, w27, #1
938 add w15, w15, w27
939 add w15, w15, #1
940 asr w15, w15, #1
941 strb w15, [x8]
942 ldrb w15, [x1]
943 ldrb w27, [x6]
944 add w15, w15, w27
945 add w15, w15, #1
946 asr w15, w15, #1
947 sxtw x27, w0
948 add x27, x27, x5
949 ldrb w27, [x27,#1]
950 ldrb w28, [x4]
951 add w27, w27, w28
952 add w27, w27, #1
953 asr w27, w27, #1
954 add w15, w15, w27
955 add w15, w15, #1
956 asr w15, w15, #1
957 strb w15, [x9]
958 sxtw x15, w0
959 add x15, x15, x5
960 ldrb w15, [x15,#1]
961 ldrb w27, [x4]
962 add w15, w15, w27
963 add w15, w15, #1
964 asr w15, w15, #1
965 sxtw x27, w0
966 add x27, x27, x5
967 ldrb w27, [x27,#2]
968 ldrb w28, [x14]
969 add w27, w27, w28
970 add w27, w27, #1
971 asr w27, w27, #1
972 add w15, w15, w27
973 add w15, w15, #1
974 asr w15, w15, #1
975 strb w15, [x11]
976 add x11, x11, #1
977 add x9, x9, #1
978 add x8, x8, #1
979 add x7, x7, #1
980 add w0, w0, #2
981 add x14, x14, #2
982 add x4, x4, #2
983 add x13, x13, #2
984 add x3, x3, #2
985 add x12, x12, #2
986 add x2, x2, #2
987 add x6, x6, #2
988 add x1, x1, #2
989 add x10, x10, #2
990 cmp x1, x18
991 blo .L.482__2
992 .L.482__1:
993 add x20, x20, x22
994 add x23, x23, x19
995 add x24, x24, x19
996 add x25, x25, x19
997 add x26, x26, x19
998 add w21, w21, #1
999 ldr w10, [x29,#16]
1000 cmp w21, w10
1001 blt .L.482__4
问题1:
w0的存在完全冗余,cvt(w0)可以外提,多引入一个iv
问题2:
SR-add做的过于激进导致IV数量激增,共14个iv
其中x11,x9,x8,x7 可归一为一个iv, 后续ldr使用[base, iv] 模式,base分别为dstc/dstv/dsth/dst0,iv为x
其中x14,x4,x6可以使用x6(src2 + 2x)作为iv x14,x4 表示为 x6 + offset模式
x13,x3,x1; x12,x2,x10同理;所以最后只需4个iv即可
修改建议:
最贴近iread/iassign的add不作为SR-add的目标,因为这样可以充分利用后端ISA能力又不引入新的iv,同时base,offset需要进行规范化,将地址计算充分的通过 分配率和结合律 将base和offset模式做成如下模式:
1) 如果地址中不不存在常数,最好将当前循环的循环不变量的计算结果作为base,其余提出作为iv,这样相同步进、初值不同的iv均可归一,x11,x9,x8,x7就是这种情况
2) 如果地址中有常数,将常数作为offset,base作为iv,如果一系列的base + const中常数偏移存在负数(-c),可将base-c作为iv外提,其余常数+c后作为offset
mc.c 函数: hpel_filter:
C源码:
static ALWAYS_INLINE uint8_t x264_clip_uint8( int x )
{
return x&(~255) ? (-x)>>31 : x;
}
#define TAPFILTER(pix, d) ((pix)[x-2*d] + (pix)[x+3*d] - 5*((pix)[x-d] + (pix)[x+2*d]) + 20*((pix)[x] + (pix)[x+d]))
static void hpel_filter( uint8_t *dsth, uint8_t *dstv, uint8_t *dstc, uint8_t *src,
int stride, int width, int height, int16_t *buf )
{
for( int y = 0; y < height; y++ )
{
...
for( int x = 0; x < width; x++ )
dstc[x] = x264_clip_uint8( (TAPFILTER(buf+2,1) + 512) >> 10 );
...
dsth += stride;
dstv += stride;
dstc += stride;
src += stride;
}
}
SR-add开启后 汇编:
2271 mov x0, #0
2272 mov x1, x25
2273 mov x2, x20
2274 sub x3, x20, #4
2275 add x4, x20, #6
2276 sub x5, x20, #2
2277 add x6, x20, #4
2278 add x7, x20, #2
2279 .L.466__11:
2280 ldrsh w8, [x2,#4]
2281 ldrsh w10, [x3,#4]
2282 ldrsh w11, [x4,#4]
2283 ldrsh w12, [x5,#4]
2284 ldrsh w13, [x6,#4]
2285 ldrsh w9, [x7,#4]
2286 add w10, w10, w11, SXTH
2287 add w11, w12, w13, SXTH
2288 add w11, w11, w11, LSL #2
2289 add w8, w8, w9, SXTH
2290 add w8, w8, w8, LSL #2
2291 lsl w8, w8, #2
2292 sub w9, w10, w11
2293 add w8, w9, w8
2294 add w8, w8, #512
2295 asr w8, w8, #10
2296 tst w8, #-256
2297 beq .L.466__8
2298 neg w8, w8
2299 asr w9, w8, #31
2300 b .L.466__9
2301 .L.466__8:
2302 mov w9, w8
2303 .L.466__9:
2304 strb w9, [x1]
2305 add x0, x0, #1
2306 add x1, x1, #1
2307 add x2, x2, #2
2308 add x7, x7, #2
2309 add x6, x6, #2
2310 add x5, x5, #2
2311 add x4, x4, #2
2312 add x3, x3, #2
2313 cmp x0, w15, SXTW
2314 blt .L.466__11
本例子最优解只需要2个IV,但实际有8个
其中x0,x1可以只是用x0为iv,x1拆分成[dstc,x0]
x2,x7,x6,x5,x4,x3 可以将buf+x作为iv, 其余的常数经过constfold合并后作为offset
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
525中的quant_4x4,sub4x4_dct均有同类问题
spec525 x264_pixel_var_16x16
C源码:
static uint64_t name( uint8_t *pix, int i_stride ) \
{ \
uint32_t sum = 0, sqr = 0; \
for( int y = 0; y < w; y++ ) \
{ \
for( int x = 0; x < w; x++ ) \
{ \
sum += pix[x]; \
sqr += pix[x] * pix[x]; \
} \
pix += i_stride; \
} \
return sum + ((uint64_t)sqr << 32); \
}
PIXEL_VAR_C( x264_pixel_var_16x16, 16 )
汇编:
4306 .L.459__10:
4307 mov w4, #0
4308 mov w5, #0
4309 mov w6, #0
4310 sxtw x7, w1
4311 .L.459__9:
4312 mov x2, #0 <==== "冗余iv 可以用x3替换"
4313 mov x3, x0
4314 .L.459__2:
4315 ldrb w1, [x3]
4316 add w4, w4, w1
4317 madd w5, w1, w1, w5
4318 add x2, x2, #1
4319 add x3, x3, #1
4320 cmp x2, #16
4321 blt .L.459__2
4322 .L.459__1:
4323 add x0, x0, x7
4324 add w6, w6, #1
4325 cmp w6, #16
4326 blt .L.459__9
4327 .L.459__3:
4328 uxtw x0, w4
4329 uxtw x1, w5
4330 add x0, x0, x1, LSL #32
4331 .L.459__13:
4332 ret
提交了!854:Fine-tuned selection of SR candidates under --sradd such that add/sub to pointer value is disqualified 作为--sradd 下减少产生的IV数目的第一步. 请试试看。
PR854 合入后,“x11,x9,x8,x7 可归一为一个iv, 后续ldr使用[base, iv] 模式,base分别为dstc/dstv/dsth/dst0,iv为x” —— 这个问题已修复
“其中x14,x4,x6可以使用x6(src2 + 2x)作为iv x14,x4 表示为 x6 + offset模式
x13,x3,x1; x12,x2,x10同理;所以最后只需4个iv即可” ———— 这个问题还未修复
提交了PR875. 现在在 --sradd 下, frame_init_lowres_core()只剩下5个IV.
frame_init_lowres_core 打上PR875后的汇编:
804 .L.482__4:
805 cmp w21, #0
806 ble .L.482__1
807 mov w4, #0
808 lsl w3, w21, #1 // w3 = 2 * width
809 mov x1, #0 // x1 = x
810 add x2, x9, w19, SXTW // x2 = src1 = src0 + src_stride
811 mov x0, #0 // x0 = 2 * x
812 add x5, x2, w19, SXTW // x5 = src2 = src1 + src_stride
813 sxtw x10, w3 // x10 = 2 * width
814 mov x7, #1
815 mov x8, #2
816 .L.482__2:
817 add x6, x9, x0 // x6 = src0 + 2x
818 add x3, x2, x0 // x3 = src1 + 2x
819 ldrb w22, [x9,x0]
820 ldrb w23, [x2,x0]
821 add w22, w22, w23
822 add w22, w22, #1
823 asr w22, w22, #1
824 ldrb w23, [x6,#1]
825 ldrb w24, [x3,#1]
826 add w23, w23, w24
827 add w23, w23, #1
828 asr w23, w23, #1
829 add w22, w22, w23
830 add w22, w22, #1
831 asr w22, w22, #1
832 strb w22, [x11,x1]
833 ldrb w22, [x6,#1]
834 ldrb w23, [x3,#1]
835 add w22, w22, w23
836 add w22, w22, #1
837 asr w22, w22, #1
838 ldrb w6, [x6,#2]
839 ldrb w23, [x3,#2]
840 add w6, w6, w23
841 add w6, w6, #1
842 asr w6, w6, #1
843 add w6, w22, w6
844 add w6, w6, #1
845 asr w6, w6, #1
846 strb w6, [x12,x1]
847 ldrb w6, [x2,x0]
848 ldrb w22, [x5,x0]
849 add w6, w6, w22
850 add w6, w6, #1
851 asr w6, w6, #1
852 ldrb w3, [x3,#1]
853 ldrb w22, [x5,x7]
854 add w3, w3, w22
855 add w3, w3, #1
856 asr w3, w3, #1
857 add w3, w6, w3
858 add w3, w3, #1
859 asr w3, w3, #1
860 strb w3, [x13,x1]
861 add x3, x2, w4, SXTW // x3 = src1 + 2 * x
862 ldrb w3, [x3,#1]
863 ldrb w6, [x5,x7]
864 add w3, w3, w6
865 add w3, w3, #1
866 asr w3, w3, #1
867 add x6, x2, w4, SXTW // x6 = src1 + 2 * x
868 ldrb w6, [x6,#2]
869 ldrb w22, [x5,x8]
870 add w6, w6, w22
871 add w6, w6, #1
872 asr w6, w6, #1
873 add w3, w3, w6
874 add w3, w3, #1
875 asr w3, w3, #1
876 strb w3, [x14,x1]
877 add x1, x1, #1
878 add w4, w4, #2
879 add x8, x8, #2
880 add x7, x7, #2
881 add x0, x0, #2
882 cmp x0, x10
883 blt .L.482__2
看似iv = iv + const这种pattern的iv数量降低到5个,但是这5个iv选择的并不最优,缺少合理的选择逻辑,并且在loop中多出了由这些iv组成的add,形成secondary iv,所以本质上和9个iv一样
比如 817 add x6, x9, x0 // x6 = src0 + 2x
、818 add x3, x2, x0 // x3 = src1 + 2x
、861 add x3, x2, w4, SXTW // x3 = src1 + 2 * x
、867 add x6, x2, w4, SXTW // x6 = src1 + 2 * x
问题1:
817 add x6, x9, x0 // x6 = src0 + 2x
、818 add x3, x2, x0 // x3 = src1 + 2x
和848 ldrb w22, [x5,x0]
中的x5 + x0 应该外提作为iv,这样消除x0
问题2:
861 add x3, x2, w4, SXTW // x3 = src1 + 2 * x
、867 add x6, x2, w4, SXTW // x6 = src1 + 2 * x
这两个是一样的且和818 add x3, x2, x0 // x3 = src1 + 2x
逻辑完全一致,所以完全不需要存在w4单独作为iv,这样消除line 861和line 867的secondary iv和w4
问题3:
814 mov x7, #1
和815 mov x8, #2
这2个iv存在的很奇怪,看C源码我们知道这段逻辑对于scr0/src1/src2是按照一样的index pattern进行访问的,但是我们最后选择的iv却形式不同,src0和src1选择的组合方式基本是src + 2x作为iv,后续内存访问使用[iv, const offset],而src2的访问选择2x + const offset作为iv,后续内存访问使用[src2, iv]
这样选择的问题是只要const offset 初值不同就要引入新的iv,且无法最大化利用和src0,src1中相似的部分
建议:
这些问题本质还是现在的pre sradd只能做到识别、提出iv,但是缺少分析指导什么样的提出iv才是最优的,比如将相似的iv收集为一个group,每个group提出一个iv,每个group内找到iv的最优解,简单的增加些过滤条件是达不到要求的
之前提到的525函数中的同类问题仍然存在,原因一致
看来目标试要从5个IV降低到2个IV, 第一个基于x, 第二个基于2*x,你认同吗?
对于dst0[x]、dsth[x]、dstv[x]、dstc[x]使用x作为iv、
对于src0,src1,src2的访问,地址计算由三部分相加组成:
因为后端ldr指令mem里可以保留2个变量,所以这三个组成部分中的2个必须在loop外相加合并为一个变量。
如果选择1,3在loop外合并就需要计算3*3个变量,即
x0 = src0 + 0
x1 = src0 + 1
x2 = src0 + 2
...
x7 = src2 + 1
x8 = src2 + 2
x9 = 0 // 作为 iv 2 *x的初值
loop_begin:
ldr xx,[x0,x9]
ldr xx,[x1,x9]
ldr xx,[x2,x9]
...
ldr xx,[x8,x9]
add x9, x9, #2
goto loop_begin
这样选择会明显增大寄存器压力;
如果选择1,2在loop外进行合并,则由1个iv变为3个iv,但是由于src1,src2本身就会在loop外计算所以不会引入新的计算和寄存器压力,最后的汇编大致如下:
w19 = src_stride
mov x1, src0 // x1 = src0 + 2x 作为iv
add x2, x1, w19, SXTW // x2 = src1 + 2 * x 作为iv, 即使不做为iv也会有此计算
add x3, x2, w19, SXTW // x5 = src2 + 2 * x 同上
loop_begin:
ldr xx,[x1]
ldr xx, [x1, #1]
...
ldr xx, [x3]
ldr xx, [x3, #1]
ldr xx, [x3, #2]
add x1, x1, #2
add x2, x2, #2
add x3, x3, #2
goto loop_head
所以从iv数量上看2个是最优解,但是综合考虑可能4个效果更好
刚更新了 PR875. 现在只需两个IV.
最新的汇编:
790 .L.482__4:
791 cmp w7, #0
792 ble .L.482__1
793 lsl w8, w7, #1
794 mov x9, #0
795 add x11, x0, w5, SXTW
796 mov x6, #0
797 add x12, x11, w5, SXTW
798 sxtw x13, w8
799 .L.482__2:
800 add x10, x0, x6 <====== "x0为不变量,x6为iv, x10本质与iv一致"
801 add x8, x11, x6 <====== "x11为不变量,x6为iv, x8本质与iv一致"
802 ldrb w19, [x0,x6]
803 ldrb w20, [x11,x6]
804 add w19, w19, w20
805 add w19, w19, #1
806 asr w19, w19, #1
807 ldrb w20, [x10,#1]
808 ldrb w21, [x8,#1]
809 add w20, w20, w21
810 add w20, w20, #1
811 asr w20, w20, #1
812 add w19, w19, w20
813 add w19, w19, #1
814 asr w19, w19, #1
815 strb w19, [x1,x9]
...
830 add x10, x12, x6 <====== "x12为不变量,x6为iv, x10本质与iv一致"
831 ldrb w19, [x11,x6]
832 ldrb w20, [x12,x6]
833 add w19, w19, w20
834 add w19, w19, #1
835 asr w19, w19, #1
836 ldrb w20, [x8,#1]
837 ldrb w21, [x10,#1]
838 add w20, w20, w21
839 add w20, w20, #1
840 asr w20, w20, #1
841 add w19, w19, w20
842 add w19, w19, #1
843 asr w19, w19, #1
844 strb w19, [x3,x9]
...
859 add x9, x9, #1
860 add x6, x6, #2
861 cmp x6, x13
862 blt .L.482__2
就像之前说的现在只是 iv = iv + const这种明显的iv数量是2个,但是有3个潜在iv,已在汇编中标出,现在本质是5个iv
源代码是: src1[2*x+1]
现在 (2x)是iv, src1是循环不变,最理想是把表达式排成 (src1 + 1) + (2x), 那(src1 + 1)就可以在循环外计算,这是你所要求的。
但src1循环不变还是x循环不变,我们事前是不知道的,如果循环里有(src1++),那最理想是 src1 + (2x+1)的排序,让(2x+1)可以搬到循环外,而src1是iv。这是典型的 reassociation 和 CSE 优化相互关系的问题:
(a+b)+c 和 (b+c) 之间, 编译器没法辨认 (b+c) 是 CSE。
如果对性能没负面影响的话,最好把 --sradd 默认打开。
我有有个主意可以解决以上reassociation的问题,不需加新的phase做复杂的分析。待PR875合入后我会再做下一步。
更新了PR875. 现在基于src1 在函数里面有没有++/--, 没有的花, 会把 (src1+2x)+1 reassociate 成 (src1+1)+2x, 让 (src1+1) 可以被移到循环外。
以上reassociation 和 --sradd 选项并没绑定,默认也会做。
frame_init_lowres_core()现在还是2个IV,假IV因为以上的reassociation现在都消失了。
spec 525 C源码:
#define TAPFILTER(pix, d) ((pix)[x-2*d] + (pix)[x+3*d] - 5*((pix)[x-d] + (pix)[x+2*d]) + 20*((pix)[x] + (pix)[x+d]))
static void hpel_filter( uint8_t *dsth, uint8_t *dstv, uint8_t *dstc, uint8_t *src,
int stride, int width, int height, int16_t *buf )
{
for( int y = 0; y < height; y++ )
{
for( int x = -2; x < width+3; x++ ) <===== "loop 1"
{
int v = TAPFILTER(src,stride);
dstv[x] = x264_clip_uint8( (v + 16) >> 5 );
buf[x+2] = v;
}
for( int x = 0; x < width; x++ ) <===== "loop 2"
dstc[x] = x264_clip_uint8( (TAPFILTER(buf+2,1) + 512) >> 10 );
for( int x = 0; x < width; x++ ) <===== "loop 3"
dsth[x] = x264_clip_uint8( (TAPFILTER(src,1) + 16) >> 5 );
dsth += stride;
dstv += stride;
dstc += stride;
src += stride;
}
}
这里有三个loop1,2,3在打上PR和打开--sradd后汇编如下,
loop 1:
2335 .L.466__17:
2336 .loc 19 189
2337 cmn w19, #2
2338 ble .L.466__6
2339 add w9, w4, w4, LSL #1 // w9 = stride * 3
2340 lsl w10, w4, #1 // w10 = stride * 2
2341 add x17, x7, #4 // x17 = buf + 2 * sizeof(buf)
2342 neg w8, w4 // w8 = -stride
2343 sub w11, w8, #2 // w11 = - stride - 2
2344 mov x8, #-2 // x8 = x iv
2345 sub w12, w4, #2 // w12 = stride - 2
2346 sub w13, w9, #2 // w13 = stride * 3 - 2
2347 neg w9, w4, LSL #1 // w9 = -2 * stride
2348 sub w14, w9, #2 // w14 = -2 * stride - 2
2349 sub w15, w10, #2 // w15 = 2 * stride - 2
2350 sxtw x10, w11 // x10 = - stride - 2 iv
2351 mov x9, #-4 // x9 = 2 * x iv
2352 sxtw x21, w19 // x21 = width + 3
2353 sxtw x11, w12 // x11 = stride - 2 iv
2354 sxtw x12, w13 // x12 = stride * 3 - 2 iv
2355 sxtw x13, w14 // x13 = -2 * stride - 2 iv
2356 sxtw x14, w15 // x14 = 2 * stride - 2 iv
2357 lsl x15, x21, #1 // x15 = 2 * (width + 3)
2358 .L.466__7:
2359 ldrb w21, [x3,x8]
2360 ldrb w22, [x3,x10]
2361 ldrb w23, [x3,x11]
2362 ldrb w24, [x3,x12]
2363 ldrb w25, [x3,x13]
2364 ldrb w26, [x3,x14]
2365 add w21, w21, w23
2366 add w21, w21, w21, LSL #2
2367 lsl w21, w21, #2
2368 add w23, w25, w24
2369 add w22, w22, w26
2370 add w22, w22, w22, LSL #2
2371 sub w22, w23, w22
2372 add w22, w22, w21
2373 .loc 19 191
2374 add w21, w22, #16
2375 asr w21, w21, #5
2376 .loc 13 197
2377 tst w21, #-256
2378 beq .L.466__4
2379 neg w21, w21
2380 asr w21, w21, #31
2381 .L.466__4:
2382 .loc 19 192
2383 strb w21, [x1,x8]
2384 .loc 19 193
2385 strh w22, [x17,x9]
2386 add x14, x14, #1
2387 add x13, x13, #1
2388 add x12, x12, #1
2389 add x11, x11, #1
2390 add x8, x8, #1
2391 add x9, x9, #2
2392 add x10, x10, #1
2393 cmp x9, x15
2394 blo .L.466__7
问题:
当前第一个loop里iv 有7个,最优解只需2-3个iv即可。 loop内array访问有三类
loop 2:
2397 cmp w5, #0
2398 ble .L.466__14
2399 add x11, x7, #6 // x11 = buf + 3 * size
2400 add x12, x7, #8 // x12 = buf + 4 * size
2401 add x13, x7, #10 // x13 = buf + 5 * size
2402 add x10, x7, #4 // x10 = buf + 2 * size
2403 mov x9, #0 // x9 = x <==== "iv"
2404 mov x8, #0 // x8 = 2 * x <==== "iv"
2405 .L.466__11:
2406 add x14, x8, x10 // x14 = buf + (x + 2) * size <==== "等同iv"
2407 ldrsh w15, [x8,x10]
2408 sub x17, x14, #2 // x17 = buf + (x + 1) * size <==== "等同iv"
2409 ldrsh w17, [x17]
2410 sub x14, x14, #4 // x14 = buf + (x + 0) * size <==== "等同iv"
2411 ldrsh w14, [x14]
2412 ldrsh w21, [x13,x8] <==== "符合预期"
2413 add w21, w14, w21
2414 ldrsh w14, [x12,x8] <==== "符合预期"
2415 add w14, w17, w14
2416 add w17, w14, w14, LSL #2
2417 ldrsh w14, [x11,x8] <==== "符合预期"
2418 add w14, w15, w14
2419 add w14, w14, w14, LSL #2
2420 lsl w14, w14, #2
2421 sub w15, w21, w17
2422 add w14, w15, w14
2423 add w14, w14, #512
2424 asr w14, w14, #10
2425 .loc 13 197
2426 tst w14, #-256
2427 beq .L.466__8
2428 neg w14, w14
2429 asr w14, w14, #31
2430 .L.466__8:
2431 .loc 19 196
2432 strb w14, [x2,x9]
2433 add x9, x9, #1
2434 add x8, x8, #2
2435 cmp x9, w5, SXTW
2436 blt .L.466__11
问题:
此loop最优需2个iv,当前实际等同5个iv
主要的array访问pattern是 buf + 2 * x + const,预期是 buf + const合并,2 * x 作为iv,loop内有3个访问符合预期,另外3个引入了类似iv的变量
loop 3:
2441 mov x13, #1
2442 mov x14, #2
2443 mov x8, #0 // x8 = x
2444 sxtw x15, w5
2445 mov x9, #3 // x9 = x + 3
2446 mov x10, #2 // x10 = x + 2
2447 mov x11, #1 // x11 = x + 1
2448 .L.466__15:
2449 add x17, x8, x3 // x17 = src + x + 0 <==== "等同iv"
2450 ldrb w12, [x8,x3]
2451 sub x21, x17, x14 // x21 = src + x - 2 <==== "等同iv"
2452 ldrb w21, [x21]
2453 sub x17, x17, x13 // x17 = src + x - 1 <==== "等同iv"
2454 ldrb w17, [x17]
2455 ldrb w22, [x9,x3] <==== "符合预期"
2456 ldrb w23, [x10,x3] <==== "符合预期"
2457 ldrb w24, [x11,x3] <==== "符合预期"
2458 add w21, w21, w22
2459 add w17, w17, w23
2460 add w17, w17, w17, LSL #2
2461 add w12, w12, w24
2462 add w12, w12, w12, LSL #2
2463 lsl w12, w12, #2
2464 sub w17, w21, w17
2465 add w12, w17, w12
2466 add w12, w12, #16
2467 asr w12, w12, #5
2468 tst w12, #-256
2469 beq .L.466__12
2470 neg w12, w12
2471 asr w12, w12, #31
2472 .L.466__12:
2473 .loc 19 198
2474 strb w12, [x0,x8]
2475 add x11, x11, #1
2476 add x10, x10, #1
2477 add x9, x9, #1
2478 add x8, x8, #1
2479 cmp x8, x15
2480 blt .L.466__15
问题同loop 2
剩下的问题是两个通用优化的问题,间接影响到strength reduction的收益,已经提交了!890修好了:
问题一: cvt a64 i64 应该是冗余的,不须参生。
问题二: (i - 2) 可以把(-2)抽出来和offset合并。
PR890 也是没跟--sradd绑定。
spec 525 hpel_filter的问题仍存在:
loop 1源码见上个回复,当前汇编如下:
1982 .L.466__17:
1983 cmn w19, #2
1984 ble .L.466__6
1985 add w9, w4, w4, LSL #1
1986 lsl w10, w4, #1
1987 add x17, x7, #4
1988 neg w8, w4
1989 sub w11, w8, #2
1990 mov x8, #-2
1991 sub w12, w4, #2
1992 sub w13, w9, #2
1993 neg w9, w4, LSL #1
1994 sub w14, w9, #2
1995 sub w21, w10, #2
1996 sxtw x10, w11
1997 mov x9, #-4
1998 sxtw x15, w19
1999 sxtw x11, w12
2000 sxtw x12, w13
2001 sxtw x13, w14
2002 sxtw x14, w21
2003 .L.466__7:
2004 ldrb w21, [x3,x8]
2005 ldrb w22, [x3,x10]
2006 ldrb w23, [x3,x11]
2007 ldrb w24, [x3,x12]
2008 ldrb w25, [x3,x13]
2009 ldrb w26, [x3,x14]
2010 add w21, w21, w23
2011 add w23, w21, w21, LSL #2
2012 add w24, w25, w24
2013 add w21, w22, w26
2014 add w21, w21, w21, LSL #2
2015 sub w21, w24, w21
2016 add w22, w21, w23, LSL #2
2017 add w21, w22, #16
2018 asr w21, w21, #5
2019 tst w21, #-256
2020 beq .L.466__4
2021 neg w21, w21
2022 asr w21, w21, #31
2023 .L.466__4:
2024 strb w21, [x1,x8]
2025 strh w22, [x17,x9]
2026 add x14, x14, #1 <===== "iv"
2027 add x13, x13, #1 <===== "iv"
2028 add x12, x12, #1 <===== "iv"
2029 add x11, x11, #1 <===== "iv"
2030 add x8, x8, #1 <===== "iv"
2031 add x9, x9, #2 <===== "iv"
2032 add x10, x10, #1 <===== "iv"
2033 cmp x9, x15, LSL #1
2034 blo .L.466__7
问题:( 主要问题 )
当前第一个loop里iv 有7个,最优解只需2-3个iv即可。 loop内array访问有三类
1.src + x + n * stride (n可以为-2,-1,0,1,2,3)
2.dstv + x
3.buf + (x + 2) * sizeof(int16) = buf + 2x + 4
每一类提出一个iv,2,3可以整体作为iv;
1中最好应该是 src + x,当前选择是x + n * stride作为iv,所以iv数量变多
loop 3:
2071 .L.466__10:
2072 cmp w5, #0
2073 ble .L.466__14
2074 sub x9, x3, #2
2075 mov x8, #0
2076 sxtw x10, w5
2077 add x11, x3, #3
2078 add x12, x3, #2
2079 add x13, x3, #1
2080 .L.466__15:
2081 ldrb w14, [x9,x8]
2082 add x15, x3, x8 <===== "等同iv"
2083 ldrb w17, [x3,x8]
2084 sub x15, x15, #1 <===== "等同iv 应将x3 - 1 外提类似 x9"
2085 ldrb w15, [x15]
2086 ldrb w21, [x11,x8]
2087 ldrb w22, [x12,x8]
2088 ldrb w23, [x13,x8]
2089 add w21, w14, w21
2090 add w14, w15, w22
2091 add w15, w14, w14, LSL #2
2092 add w14, w17, w23
2093 add w14, w14, w14, LSL #2
2094 sub w15, w21, w15
2095 add w14, w15, w14, LSL #2
2096 add w14, w14, #16
2097 asr w14, w14, #5
2098 tst w14, #-256
2099 beq .L.466__12
2100 neg w14, w14
2101 asr w14, w14, #31
2102 .L.466__12:
2103 strb w14, [x0,x8]
2104 add x8, x8, #1
2105 cmp x8, x10
2106 blt .L.466__15
之前做的re-association只局限于常数的操作数,我准备扩展到不局限常数,就可以解决以上的遗留问题。
新的reassociation功能已经在刚提交的PR893实现。reassociation 和 --sradd 是没有绑定的。
现在 --sradd 之下, loop1 IV 2个, loop2 IV 2个, loop3 IV 1个,假IV都没有了。
没遗留问题的话,请把 --sradd 设为默认。
hpel_filter:
经过几次调整,loop内结构几乎达到最优,但是从整个函数来看还有优化空间,主要是寄存器使用效率上。
GCC在此函数寄存器压力明显小于maple的。因为为达到spill的程度,所以并未引起明显劣化。
GCC汇编:
40 stp x29, x30, [sp, -48]!
44 add x29, sp, 0
46 stp x19, x20, [sp, 16]
47 stp x21, x22, [sp, 32]
maple汇编:
1942 stp x29, x30, [sp,#-80]!
1943 stp x19, x20, [sp,#16]
1944 stp x21, x22, [sp,#32]
1945 stp x23, x24, [sp,#48]
1946 str x25, [sp,#64]
以Loop2为例,maple汇编:
// x7 = buf
// x2 = dstc
1997 add x10, x7, #6 <====== "冗余"
1998 add x11, x7, #4 <====== "冗余"
1999 add x12, x7, #8 <====== "冗余"
2000 add x13, x7, #2 <====== "冗余"
2001 add x14, x7, #10 <====== "冗余"
2002 mov x9, #0 <====== "冗余"
2003 mov x8, #0 <====== "冗余"
2004 .L.466__11:
2005 // inlining begin: FUNC x264_clip_uint8
2006 ldrsh w15, [x7,x8]
2007 ldrsh w17, [x14,x8]
2008 add w17, w15, w17
2009 ldrsh w15, [x13,x8]
2010 ldrsh w21, [x12,x8]
2011 add w15, w15, w21
2012 add w21, w15, w15, LSL #2
2013 ldrsh w15, [x11,x8]
2014 ldrsh w22, [x10,x8]
2015 add w15, w15, w22
2016 add w15, w15, w15, LSL #2
2017 sub w17, w17, w21
2018 add w15, w17, w15, LSL #2
2019 add w15, w15, #512
2020 asr w15, w15, #10
2021 tst w15, #-256
2022 beq .L.466__8
2023 neg w15, w15
2024 asr w15, w15, #31
2025 .L.466__8:
2026 // inlining end: FUNC x264_clip_uint8
2027 strb w15, [x2,x9]
2028 add x9, x9, #1 <====== "冗余"
2029 add x8, x8, #2
2030 cmp x9, w5, SXTW
2031 blt .L.466__11
改进1:
1997 add x10, x7, #6 <====== "冗余"
1998 add x11, x7, #4 <====== "冗余"
1999 add x12, x7, #8 <====== "冗余"
2000 add x13, x7, #2 <====== "冗余"
2001 add x14, x7, #10 <====== "冗余"
...
2003 mov x8, #0 <====== "冗余"
2006 ldrsh w15, [x7,x8]
2007 ldrsh w17, [x14,x8]
2009 ldrsh w15, [x13,x8]
2010 ldrsh w21, [x12,x8]
2013 ldrsh w15, [x11,x8]
2014 ldrsh w22, [x10,x8]
...
可变为:
mov x10, x7
...
2006 ldrsh w15, [x10]
2007 ldrsh w17, [x10,#10]
2009 ldrsh w15, [x10,#2]
2010 ldrsh w21, [x10,#8]
2013 ldrsh w15, [x10,#4]
2014 ldrsh w22, [x10,#6]
add x10, x10, #2
建议:地址由 buf + x + const类组成,当前是 buf + const外提作为base,但是由于const初值过多导致寄存器压力变大,buf + x外提作为iv更合适
改进2:
2002 mov x9, #0 <====== "冗余"
...
2027 strb w15, [x2,x9]
2028 add x9, x9, #1 <====== "冗余"
可变为:
mov x9, x2
...
2027 strb w15, [x9], #1
建议:之前建议过作为iassign和iread的地址计算的最后一层add不要外提,以充分利用memopnd的能力,但是这个标准不能一刀切,当在循环内且外提不会导致iv数量增加(没有副作用)时是可以外提的,这样为2027中的pre/post inc提供机会(这个后端优化在规划中,当前方式阻碍了这个优化的效果)
另外,扩大化的变量re-association的PR引入了一些其他问题,以下是2个例子:
spec557----lz_encoder_mf.c----bt_skip_func
LOC 24 545
dassign %pair_545_3 0 (add ptr (
dread ptr %son,
cvt ptr i32 (mul i32 (
shl u32 (
add u32 (
sub u32 (dread u32 %cyclic_pos, dread u32 %delta_538_3),
dread u32 %levVar_722),
constval i32 1),
constval i32 4))))
......
......
LOC 24 565
dassign %ptr1_532_2 0 (add ptr (
dread ptr %pair_545_3,
cvt ptr i32 (mul i32 (constval i32 1, constval i32 4))))
LOC 24 566
dassign %cur_match 0 (iread u32 <* u32> 0 (dread ptr %ptr1_532_2))
irmap中把%ptr1_532_2
的右值连同%pair_545_3
的右值传播进了iread,在simplifyivar的时候reassociate了这个base,但是的%ptr1_532_2
的右值并没有被reassociate,导致此处的冗余计算没有被pre外提(已经是两个不同的表达式了)。
||MEIR|| dassign VAR %ptr1_532_2{offset:0}<0> (field)0 mx222
rhs = OP add ptr kPtyInvalid mx189
opnd[0] = OP add ptr kPtyInvalid mx120
opnd[0] = VAR %son{offset:0}<0> (field)0 mx1<Z>
opnd[1] = OP cvt ptr i32 mx119
opnd[0] = OP mul i32 kPtyInvalid mx118
opnd[0] = OP shl u32 kPtyInvalid mx117
opnd[0] = OP add u32 kPtyInvalid mx116
opnd[0] = OP sub u32 kPtyInvalid mx115
opnd[0] = VAR %cyclic_pos{offset:0}<0> (field)0 mx2<Z>
opnd[1] = VAR %delta_538_3{offset:0}<0> (field)0 mx43
opnd[1] = VAR %levVar_722{offset:0}<0> (field)0 mx57
opnd[1] = CONST 1 mx3
opnd[1] = CONST 4 mx5
opnd[1] = CONST 4 mx9
||MEIR|| dassign VAR %cur_match{offset:0}<0> (field)0 mx223
rhs = IVAR mx225 u32 TYIDX:146<* u32> (field)0
base = OP add ptr kPtyInvalid mx192
opnd[0] = OP add ptr kPtyInvalid mx191
opnd[0] = VAR %son{offset:0}<0> (field)0 mx1<Z>
opnd[1] = CONST 4 mx9
opnd[1] = OP cvt ptr i32 mx119
opnd[0] = OP mul i32 kPtyInvalid mx118
opnd[0] = OP shl u32 kPtyInvalid mx117
opnd[0] = OP add u32 kPtyInvalid mx116
opnd[0] = OP sub u32 kPtyInvalid mx115
opnd[0] = VAR %cyclic_pos{offset:0}<0> (field)0 mx2<Z>
opnd[1] = VAR %delta_538_3{offset:0}<0> (field)0 mx43
opnd[1] = VAR %levVar_722{offset:0}<0> (field)0 mx57
opnd[1] = CONST 1 mx3
opnd[1] = CONST 4 mx5
- MU: {VAR %ptr1_532_2{offset:0}<1> (field)0 mx217}
====>分界线<====
spec557----liblzma/lzma/lzma_encoder_optimum_normal.c----lzma_lzma_optimum_normal
============BB id:9 condgoto [ InLoop ]===============
@@124
||MEIR|| dassign VAR %_374___344___341__prob_1_1_0{offset:0}<0> (field)0 mx1637
rhs = IVAR mx2525 u32 TYIDX:72<* u16> (field)0
base = OP add u64 kPtyInvalid mx2524
opnd[0] = OP add ptr kPtyInvalid mx2523
opnd[0] = OP iaddrof ptr kPtyInvalid mx2504
opnd[0] = VAR %pcoder{offset:0}<0> (field)0 mx1<Z>
opnd[1] = OP mul ptr kPtyInvalid mx2519
opnd[0] = OP cvt ptr i32 mx2518
opnd[0] = VAR %_374___344__symbol_1_0{offset:0}<0> (field)0 mx1631
opnd[1] = CONST 2 mx1228
opnd[1] = OP mul ptr kPtyInvalid mx2252
opnd[0] = OP cvt ptr i32 mx1623
opnd[0] = VAR %_374__len_to_pos_state_140_7_0{offset:0}<0> (field)0 mx1507
opnd[1] = CONST 128 mx1477
- MU: {VAR %_374___344__probs_1_0<1> (field)0 mx2414}
在上面的ivar中mx2252是循环不变量,mx2504也是循环不变量,base = mx2504 + mx2519 + mx2252。两个循环不变量的加法可以合并,外提,减少循环内冗余加法。基线版本是合并外提了的。
spec 525 dct.c sub4x4_dct
C源码:
static inline void pixel_sub_wxh( int16_t *diff, int i_size,
uint8_t *pix1, int i_pix1, uint8_t *pix2, int i_pix2 )
{
for( int y = 0; y < i_size; y++ )
{
for( int x = 0; x < i_size; x++ )
diff[x + y*i_size] = pix1[x] - pix2[x];
pix1 += i_pix1;
pix2 += i_pix2;
}
}
static void sub4x4_dct( int16_t dct[16], uint8_t *pix1, uint8_t *pix2 )
{
int16_t d[16];
int16_t tmp[16];
pixel_sub_wxh( d, 4, pix1, FENC_STRIDE, pix2, FDEC_STRIDE );
...
...
}
Maple汇编:
173 sub4x4_dct:
174 .L.447__19:
175 stp x29, x30, [sp,#-80]!
176 // inlining begin: FUNC pixel_sub_wxh
177 mov w7, #0
178 .L.447__18:
179 mov x3, #0
180 sxtw x4, w7 <==== "冗余"
181 lsl x4, x4, #1 <==== "冗余"
182 .L.447__3:
183 ldrb w5, [x1,x3]
184 ldrb w6, [x2,x3]
185 sub w5, w5, w6
186 add x6, sp, #16 <==== "冗余"
187 strh w5, [x6,x4]
188 add x4, x4, #2
189 add x3, x3, #1
190 cmp x3, #4
191 blt .L.447__3
192 .L.447__2:
193 add x1, x1, #16
194 add x2, x2, #32
195 add w7, w7, #4 <==== "冗余"
196 cmp w7, #16
197 blt .L.447__18
198 .L.447__4:
199 // inlining end: FUNC pixel_sub_wxh
....
问题1(主要) :
此循环为2层循环,diff[x + y*i_size]
其中x为内层iv,y为外层循环iv,由于当前分析比较粗粒度,并没有区分y在内存循环实际为循环不变量,最优的方案是diff + yi_size外提作为内层循环的base,x作为iv,同时在外层循环中将diff + yi_size作为iv
问题2(次要):
假设问题1已解决,内层循环中,虽然diff[x + y*i_size]
和pix1[x] - pix2[x]
共用同一个iv:x,但是sizeof(diff) = 2 * sizeof(pix1),如果按当前实现中端SR-mul会将其变成2个iv,但是后端memopnd有更好选择,可以支持strh w5, [xx,x3,lsl 1]
问题3(次要):
当前local var的epre暂未开启,导致在循环内出现 186 add x6, sp, #16 <==== "冗余"
这类冗余
理想汇编:
173 sub4x4_dct:
174 .L.447__19:
175 stp x29, x30, [sp,#-80]!
176 // inlining begin: FUNC pixel_sub_wxh
177 add x7, sp, #16
add x8, x1, 64
178 .L.447__18:
179 mov x3, #0
182 .L.447__3:
183 ldrb w5, [x1,x3]
184 ldrb w6, [x2,x3]
185 sub w5, w5, w6
187 strh w5, [x7,x3,lsl 1]
189 add x3, x3, #1
190 cmp x3, #4
191 blt .L.447__3
192 .L.447__2:
193 add x1, x1, #16
194 add x2, x2, #32
195 add x7, x7, #8
196 cmp x1, x8
197 blt .L.447__18
198 .L.447__4:
199 // inlining end: FUNC pixel_sub_wxh
总的来说,当前have_address,或者hasSelfIncDec的分析和统计粒度都太粗了。很难支持细致的tuning。
比如sub4x4_dct中的例子就是hasSelfIncDec无法分辨循环层数,将不同层次的iv混在一起;
比如地址计算中的最后一层add是否外提就需要分析所有包含次iv的地址计算,综合分析外提不会引入多余iv、会不会导致寄存器压力增加。
还是建议新增单一pass来分析iv group同时建立细致的cost model支持tuning
既然PR893 导致regression,我已把它关了,reassoc我会重新做loop-by-loop的,只针对innermost loop的iv。暂时不管outer loop.
已经提交PR898 把local var地址的PRE打开。
提交了 PR900, 这是取代PR893的。 应该解决了以上性能回退的问题。在一个新的reassoc phase做。以单个innermost loop为工作单元,所以不会受其他loop的IV影响。
请再看下这个问题,两个建议最后结果也还是2个iv
只不过通过建议1虽然还是保留++2的iv(即我优化后的x10),但是缓解了寄存器压力;
通过建议2还是++1的iv,可以使循环内的add x9, x9, 1吸收进strb w15, [x9], #1
hpel_filter:
经过几次调整,loop内结构几乎达到最优,但是从整个函数来看还有优化空间,主要是寄存器使用效率上。
GCC在此函数寄存器压力明显小于maple的。因为为达到spill的程度,所以并未引起明显劣化。
GCC汇编:
40 stp x29, x30, [sp, -48]!
44 add x29, sp, 0
46 stp x19, x20, [sp, 16]
47 stp x21, x22, [sp, 32]
maple汇编:
1942 stp x29, x30, [sp,#-80]!
1943 stp x19, x20, [sp,#16]
1944 stp x21, x22, [sp,#32]
1945 stp x23, x24, [sp,#48]
1946 str x25, [sp,#64]
以Loop2为例,maple汇编:
// x7 = buf
// x2 = dstc
1997 add x10, x7, #6 <====== "冗余"
1998 add x11, x7, #4 <====== "冗余"
1999 add x12, x7, #8 <====== "冗余"
2000 add x13, x7, #2 <====== "冗余"
2001 add x14, x7, #10 <====== "冗余"
2002 mov x9, #0 <====== "冗余"
2003 mov x8, #0 <====== "冗余"
2004 .L.466__11:
2005 // inlining begin: FUNC x264_clip_uint8
2006 ldrsh w15, [x7,x8]
2007 ldrsh w17, [x14,x8]
2008 add w17, w15, w17
2009 ldrsh w15, [x13,x8]
2010 ldrsh w21, [x12,x8]
2011 add w15, w15, w21
2012 add w21, w15, w15, LSL #2
2013 ldrsh w15, [x11,x8]
2014 ldrsh w22, [x10,x8]
2015 add w15, w15, w22
2016 add w15, w15, w15, LSL #2
2017 sub w17, w17, w21
2018 add w15, w17, w15, LSL #2
2019 add w15, w15, #512
2020 asr w15, w15, #10
2021 tst w15, #-256
2022 beq .L.466__8
2023 neg w15, w15
2024 asr w15, w15, #31
2025 .L.466__8:
2026 // inlining end: FUNC x264_clip_uint8
2027 strb w15, [x2,x9]
2028 add x9, x9, #1 <====== "冗余"
2029 add x8, x8, #2
2030 cmp x9, w5, SXTW
2031 blt .L.466__11
改进1:
1997 add x10, x7, #6 <====== "冗余"
1998 add x11, x7, #4 <====== "冗余"
1999 add x12, x7, #8 <====== "冗余"
2000 add x13, x7, #2 <====== "冗余"
2001 add x14, x7, #10 <====== "冗余"
...
2003 mov x8, #0 <====== "冗余"
2006 ldrsh w15, [x7,x8]
2007 ldrsh w17, [x14,x8]
2009 ldrsh w15, [x13,x8]
2010 ldrsh w21, [x12,x8]
2013 ldrsh w15, [x11,x8]
2014 ldrsh w22, [x10,x8]
...
可变为:
mov x10, x7
...
2006 ldrsh w15, [x10]
2007 ldrsh w17, [x10,#10]
2009 ldrsh w15, [x10,#2]
2010 ldrsh w21, [x10,#8]
2013 ldrsh w15, [x10,#4]
2014 ldrsh w22, [x10,#6]
add x10, x10, #2
建议:地址由 buf + x + const类组成,当前是 buf + const外提作为base,但是由于const初值过多导致寄存器压力变大,buf + x外提作为iv更合适
改进2:
2002 mov x9, #0 <====== "冗余"
...
2027 strb w15, [x2,x9]
2028 add x9, x9, #1 <====== "冗余"
可变为:
mov x9, x2
...
2027 strb w15, [x9], #1
建议:之前建议过作为iassign和iread的地址计算的最后一层add不要外提,以充分利用memopnd的能力,但是这个标准不能一刀切,当在循环内且外提不会导致iv数量增加(没有副作用)时是可以外提的,这样为2027中的pre/post inc提供机会(这个后端优化在规划中,当前方式阻碍了这个优化的效果)
hpel_filter:
之前loop3中的问题在PR893中消失,这次的PR900又复现
maple汇编:
2073 .L.466__15:
2074 // inlining begin: FUNC x264_clip_uint8
2075 ldrb w12, [x9,x8]
2076 add x11, x3, x8 <===== "冗余"
2077 ldrb w13, [x11,#1]
2078 ldrb w14, [x3,x8]
2079 ldrb w15, [x11,#2]
2080 sub x17, x11, #1 <===== "冗余"
2081 ldrb w17, [x17]
2082 ldrb w21, [x11,#3]
2083 add w11, w14, w13
2084 add w13, w11, w11, LSL #2
2085 add w11, w17, w15
2086 add w11, w11, w11, LSL #2
2087 add w12, w12, w21
2088 sub w11, w12, w11
2089 add w11, w11, w13, LSL #2
2090 add w11, w11, #16
2091 asr w11, w11, #5
2092 tst w11, #-256
2093 beq .L.466__13
2094 neg w11, w11
2095 asr w11, w11, #31
2096 .L.466__13:
2097 // inlining end: FUNC x264_clip_uint8
2098 strb w11, [x0,x8]
2099 add x8, x8, #1
2100 cmp x8, x10
2101 blt .L.466__15
frame_init_lowres_core中老问题仍存在:
frame_init_lowres_core:
834 .L.482__2:
835 add x8, x11, x6 <==== "伪 iv"
836 ldrb w10, [x0,x6]
837 ldrb w21, [x11,x6]
838 add w10, w10, w21
839 add w10, w10, #1
840 asr w10, w10, #1
841 ldrb w21, [x12,x6]
842 ldrb w22, [x8,#1]
843 add w21, w21, w22
844 add w21, w21, #1
845 asr w21, w21, #1
846 add w10, w10, w21
847 add w10, w10, #1
848 asr w10, w10, #1
849 strb w10, [x1,x9]
850 ldrb w10, [x12,x6]
851 ldrb w21, [x8,#1]
852 add w10, w10, w21
853 add w10, w10, #1
854 asr w10, w10, #1
855 ldrb w21, [x14,x6]
856 ldrb w22, [x8,#2]
857 add w21, w21, w22
858 add w21, w21, #1
859 asr w21, w21, #1
860 add w10, w10, w21
861 add w10, w10, #1
862 asr w10, w10, #1
863 strb w10, [x2,x9]
864 add x10, x13, x6 <==== "伪 iv"
865 ldrb w21, [x11,x6]
866 ldrb w22, [x13,x6]
867 add w21, w21, w22
868 add w21, w21, #1
869 asr w21, w21, #1
870 ldrb w22, [x8,#1]
871 ldrb w23, [x10,#1]
872 add w22, w22, w23
873 add w22, w22, #1
874 asr w22, w22, #1
875 add w21, w21, w22
876 add w21, w21, #1
877 asr w21, w21, #1
878 strb w21, [x3,x9]
879 ldrb w21, [x8,#1]
880 ldrb w22, [x10,#1]
881 add w21, w21, w22
882 add w21, w21, #1
883 asr w21, w21, #1
884 ldrb w8, [x8,#2]
885 ldrb w10, [x10,#2]
886 add w8, w8, w10
887 add w8, w8, #1
888 asr w8, w8, #1
889 add w8, w21, w8
890 add w8, w8, #1
891 asr w8, w8, #1
892 strb w8, [x4,x9]
893 add x9, x9, #1
894 add x6, x6, #2
895 cmp x6, x15
896 blt .L.482__2
同时之前提到寄存器压力的问题,这里也有体现:
maple需要使用
812 stp x19, x20, [sp,#16]
813 stp x21, x22, [sp,#32]
814 str x23, [sp,#48
gcc不需要
下面是之前我回复过的一段意见,复制在下面,具体上下文可以在上面找到。
对于dst0[x]、dsth[x]、dstv[x]、dstc[x]使用x作为iv、
对于src0,src1,src2的访问,地址计算由三部分相加组成:
因为后端ldr指令mem里可以保留2个变量,所以这三个组成部分中的2个必须在loop外相加合并为一个变量。
如果选择1,3在loop外合并就需要计算3*3个变量,即
x0 = src0 + 0
x1 = src0 + 1
x2 = src0 + 2
...
x7 = src2 + 1
x8 = src2 + 2
x9 = 0 // 作为 iv 2 *x的初值
loop_begin:
ldr xx,[x0,x9]
ldr xx,[x1,x9]
ldr xx,[x2,x9]
...
ldr xx,[x8,x9]
add x9, x9, #2
goto loop_begin
这样选择会明显增大寄存器压力;
如果选择1,2在loop外进行合并,则由1个iv变为3个iv,但是由于src1,src2本身就会在loop外计算所以不会引入新的计算和寄存器压力,最后的汇编大致如下:
w19 = src_stride
mov x1, src0 // x1 = src0 + 2x 作为iv
add x2, x1, w19, SXTW // x2 = src1 + 2 * x 作为iv, 即使不做为iv也会有此计算
add x3, x2, w19, SXTW // x5 = src2 + 2 * x 同上
loop_begin:
ldr xx,[x1]
ldr xx, [x1, #1]
...
ldr xx, [x3]
ldr xx, [x3, #1]
ldr xx, [x3, #2]
add x1, x1, #2
add x2, x2, #2
add x3, x3, #2
goto loop_head
所以从iv数量上看2个是最优解,但是综合考虑可能4个效果更好,同时在loop body内的4个iv中的3个可以通过pre/post inc吸收进ldr中,不会引入额外add
还是以前的问题,由于unrolling,将此loop展开掩盖了问题,关掉unrolling,问题重现
spec 525 dct.c sub4x4_dct
C源码:
static inline void pixel_sub_wxh( int16_t *diff, int i_size,
uint8_t *pix1, int i_pix1, uint8_t *pix2, int i_pix2 )
{
for( int y = 0; y < i_size; y++ )
{
for( int x = 0; x < i_size; x++ )
diff[x + y*i_size] = pix1[x] - pix2[x];
pix1 += i_pix1;
pix2 += i_pix2;
}
}
static void sub4x4_dct( int16_t dct[16], uint8_t *pix1, uint8_t *pix2 )
{
int16_t d[16];
int16_t tmp[16];
pixel_sub_wxh( d, 4, pix1, FENC_STRIDE, pix2, FDEC_STRIDE );
...
...
}
Maple汇编:
173 sub4x4_dct:
174 .L.447__19:
175 stp x29, x30, [sp,#-80]!
176 // inlining begin: FUNC pixel_sub_wxh
177 mov w7, #0
178 .L.447__18:
179 mov x3, #0
180 sxtw x4, w7 <==== "冗余"
181 lsl x4, x4, #1 <==== "冗余"
182 .L.447__3:
183 ldrb w5, [x1,x3]
184 ldrb w6, [x2,x3]
185 sub w5, w5, w6
186 add x6, sp, #16 <==== "冗余"
187 strh w5, [x6,x4]
188 add x4, x4, #2
189 add x3, x3, #1
190 cmp x3, #4
191 blt .L.447__3
192 .L.447__2:
193 add x1, x1, #16
194 add x2, x2, #32
195 add w7, w7, #4 <==== "冗余"
196 cmp w7, #16
197 blt .L.447__18
198 .L.447__4:
199 // inlining end: FUNC pixel_sub_wxh
....
问题1(主要) :
此循环为2层循环,diff[x + y*i_size]
其中x为内层iv,y为外层循环iv,由于当前分析比较粗粒度,并没有区分y在内存循环实际为循环不变量,最优的方案是diff + yi_size外提作为内层循环的base,x作为iv,同时在外层循环中将diff + yi_size作为iv
我的PR900 并不意图解决你以上提到的所有问题,因为那些问题是需要不同模块合作才可以达到的,一次过改动太大,合入肯定困难,所以只可以设长远的目标,以小步循序渐进。
其实PR900是与-sradd无关的,所以要不要合进去也不需看-sradd下的性能。新增的reassoc phase如果没导致regression,最好先合进去(默认也打开),那我以后调-sradd可以多一个phase帮忙做调性能的基础。
请先考虑把PR900合进去,因为这会影响后续继续调SR性能的策略。
登录 后才可以发表评论