1.4K Star 7.6K Fork 1.4K

GVP方舟编译器 / OpenArkCompiler

 / 详情

【spec525性能分析】SR-add开启后引入iv过多

待办的
成员
创建于  
2021-08-25 20:21

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

评论 (40)

Leo Young 创建了任务
展开全部操作日志

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
fredchow 负责人设置为fredchow

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 + 2x818 add x3, x2, x0 // x3 = src1 + 2x861 add x3, x2, w4, SXTW // x3 = src1 + 2 * x867 add x6, x2, w4, SXTW // x6 = src1 + 2 * x
问题1:
817 add x6, x9, x0 // x6 = src0 + 2x818 add x3, x2, x0 // x3 = src1 + 2x848 ldrb w22, [x5,x0]中的x5 + x0 应该外提作为iv,这样消除x0
问题2:
861 add x3, x2, w4, SXTW // x3 = src1 + 2 * x867 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, #1815 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的访问,地址计算由三部分相加组成:

  1. array base即src0,src1,src2
  2. iv 即2x
  3. const value 即 +0,+1,+2

因为后端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。

是的,如果在pre里确实不知道循环信息,所以我们是不是额外写个phase,这个phase里可以识别iv并引入loop信息,用来专门处理含有iv的地址运算reassociation。
在决定reassociation的方向上,我们有些偏向,比如:优先把循环不变量归为一项,循环变量归为一项,或者存在常数时,优先把const归为一项,其余为另一项。

如果这个phase在pre之前做,或许还可以为pre提供更多循环不变量外提的机会。

请县把PR875合进去,我再试做进一步的改良。

好的,会安排人做下功能和性能测试,有问题再联系你

如果对性能没负面影响的话,最好把 --sradd 默认打开。

我有有个主意可以解决以上reassociation的问题,不需加新的phase做复杂的分析。待PR875合入后我会再做下一步。

issue里的问题在sradd打开后会出现,性能测试数据劣化比较明显,暂时sradd先不打开了

更新了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访问有三类

  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 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提供机会(这个后端优化在规划中,当前方式阻碍了这个优化的效果)

你建议1和2都搞错了,x8是++2的iv, x9是++1的iv, 你提议的代码都是错的。

x8是++2的iv, x9是++1的iv 这个没错,两个建议最后结果也还是2个iv
只不过通过建议1虽然还是保留++2的iv(即我优化后的x10),但是缓解了寄存器压力;
通过建议2还是++1的iv,可以使循环内的add x9, x9, 1吸收进strb w15, [x9], #1

另外,扩大化的变量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

diff[x + yi_size] 的外层还有一个mul,这表达式是:&diff + (x + yi_size) * 2

除非我们在constantfold时引用distributive law 把它转换成 &diff + x2 + yi_size*2, 否则没法re-associate. 但distribute law的结果本身会增加多一个乘指令,所以可能导致性能回退。

当然也可以在做reassociation的时候引用distribute law,这牵涉更复杂的逻辑,往后再考虑吧。

总的来说,当前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

因为irmap.cpp:IRMap::SimplifyAddExpr()也是再做reassociation. 这里的代码把我的reassociation消掉。在PR893我把其中和我有冲突的代码注销掉,但却导致你上面提的557的性能回退,所以我在PR900不再碰SimplifyAddExpr()了。现在557再无性能回退,但loop3就没法达到我原来可以达到的效果。

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的访问,地址计算由三部分相加组成:

  1. array base即src0,src1,src2
  2. iv 即2x
  3. const value 即 +0,+1,+2

因为后端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性能的策略。

登录 后才可以发表评论

状态
负责人
里程碑
Pull Requests
关联的 Pull Requests 被合并后可能会关闭此 issue
分支
开始日期   -   截止日期
-
置顶选项
优先级
参与者(2)
C++
1
https://gitee.com/openarkcompiler/OpenArkCompiler.git
git@gitee.com:openarkcompiler/OpenArkCompiler.git
openarkcompiler
OpenArkCompiler
OpenArkCompiler

搜索帮助