This bug fixes PR 68404, which created an insn for the fusion operation when accessing an array with a large constant offset that the downstream passes (regrenam in particular don't like). Because fusion in general adds so little to the performance of power8, I just eliminated the compiler from generating this case for GCC 6. In the GCC 7 timeframe, I likely will revist fusion for power9 support. I ran a spec 2006 benchmark suite comparing the current behavior and the fix for PR 68404, and it was in the noise level (mcf was 1% slower, others ranged from 0.3% slower to 0.4% faster).
I did a bootstrap build, including a bootstrap profiled build with LTO (which is how the problem was found) and it was found. I rewrote 2 of the 3 fusion tests so that it uses fusion from a medium code toc entry instead of accessing an array element with a constant index over 65536 bytes. Is this patch ok to apply? If you would prefer, I can eliminate the code inside of the fusion_gpr_addis predicate instead of using #if 0. [gcc] 2016-02-08 Michael Meissner <meiss...@linux.vnet.ibm.com> PR target/68404 * config/rs6000/predicates.md (fusion_gpr_addis): Prevent fusing an ADDIS that adds a pointer to a large constant that sets the upper16 bits with a load operation. [gcc/testsuite] 2016-02-08 Michael Meissner <meiss...@linux.vnet.ibm.com> PR target/68404 * gcc.target/powerpc/fusion.c: Rewrite test to use TOC fusion instead accessing a really large arrray. * gcc.target/powerpc/fusion3.c: Likewise. -- Michael Meissner, IBM IBM, M/S 2506R, 550 King Street, Littleton, MA 01460-6245, USA email: meiss...@linux.vnet.ibm.com, phone: +1 (978) 899-4797
Index: gcc/config/rs6000/predicates.md =================================================================== --- gcc/config/rs6000/predicates.md (revision 233220) +++ gcc/config/rs6000/predicates.md (working copy) @@ -1716,10 +1716,17 @@ (define_predicate "fusion_gpr_addis" if (CONST_INT_P (op)) int_const = op; +#if 0 + /* PR 68404 -- regrename doesn't like: + + (mem (plus (plus (reg) + (const_int)) + (const_int)))) */ else if (GET_CODE (op) == PLUS && base_reg_operand (XEXP (op, 0), Pmode) && CONST_INT_P (XEXP (op, 1))) int_const = XEXP (op, 1); +#endif else return 0; Index: gcc/testsuite/gcc.target/powerpc/fusion3.c =================================================================== --- gcc/testsuite/gcc.target/powerpc/fusion3.c (revision 233220) +++ gcc/testsuite/gcc.target/powerpc/fusion3.c (working copy) @@ -4,15 +4,24 @@ /* { dg-skip-if "do not override -mcpu" { powerpc*-*-* } { "-mcpu=*" } { "-mcpu=power7" } } */ /* { dg-options "-mcpu=power7 -mtune=power9 -O3" } */ -#define LARGE 0x12345 +#define SIZE 4 +struct foo { + float f; + double d; +}; -int fusion_float_read (float *p){ return p[LARGE]; } -int fusion_double_read (double *p){ return p[LARGE]; } +static struct foo st[SIZE]; +struct foo *ptr_st = &st[0]; -void fusion_float_write (float *p, float f){ p[LARGE] = f; } -void fusion_double_write (double *p, double d){ p[LARGE] = d; } +float fusion_float_read (void){ return st[SIZE].f; } +double fusion_float_extend (void){ return (double)st[SIZE].f; } +double fusion_double_read (void){ return st[SIZE].d; } -/* { dg-final { scan-assembler "load fusion, type SF" } } */ -/* { dg-final { scan-assembler "load fusion, type DF" } } */ -/* { dg-final { scan-assembler "store fusion, type SF" } } */ -/* { dg-final { scan-assembler "store fusion, type DF" } } */ +void fusion_float_write (float f){ st[SIZE].f = f; } +void fusion_float_truncate (double d){ st[SIZE].f = (float)d; } +void fusion_double_write (double d){ st[SIZE].d = d; } + +/* { dg-final { scan-assembler-times "load fusion, type SF" 2 } } */ +/* { dg-final { scan-assembler-times "load fusion, type DF" 1 } } */ +/* { dg-final { scan-assembler-times "store fusion, type SF" 2 } } */ +/* { dg-final { scan-assembler-times "store fusion, type DF" 1 } } */ Index: gcc/testsuite/gcc.target/powerpc/fusion.c =================================================================== --- gcc/testsuite/gcc.target/powerpc/fusion.c (revision 233220) +++ gcc/testsuite/gcc.target/powerpc/fusion.c (working copy) @@ -1,17 +1,28 @@ -/* { dg-do compile { target { powerpc*-*-* } } } */ +/* { dg-do compile { target { powerpc*-*-* && lp64 } } } */ /* { dg-skip-if "" { powerpc*-*-darwin* } { "*" } { "" } } */ /* { dg-require-effective-target powerpc_p8vector_ok } */ /* { dg-skip-if "do not override -mcpu" { powerpc*-*-* } { "-mcpu=*" } { "-mcpu=power7" } } */ -/* { dg-options "-mcpu=power7 -mtune=power8 -O3" } */ +/* { dg-options "-mcpu=power7 -mtune=power8 -O3 -mcmodel=medium" } */ -#define LARGE 0x12345 +#define SIZE 4 +struct foo { + unsigned char uc; + signed char sc; + unsigned short us; + short ss; + int i; + unsigned u; +}; -int fusion_uchar (unsigned char *p){ return p[LARGE]; } -int fusion_schar (signed char *p){ return p[LARGE]; } -int fusion_ushort (unsigned short *p){ return p[LARGE]; } -int fusion_short (short *p){ return p[LARGE]; } -int fusion_int (int *p){ return p[LARGE]; } -unsigned fusion_uns (unsigned *p){ return p[LARGE]; } +static struct foo st[SIZE]; +struct foo *ptr_st = &st[0]; + +int fusion_uchar (void){ return st[SIZE-1].uc; } +int fusion_schar (void){ return st[SIZE-1].sc; } +int fusion_ushort (void){ return st[SIZE-1].us; } +int fusion_short (void){ return st[SIZE-1].ss; } +int fusion_int (void){ return st[SIZE-1].i; } +unsigned fusion_uns (void){ return st[SIZE-1].u; } /* { dg-final { scan-assembler-times "gpr load fusion" 6 } } */ /* { dg-final { scan-assembler-times "lbz" 2 } } */