From 921f2deea4f6f491d7e225d70d3787e8b490b18e Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Thu, 7 Feb 2013 13:07:37 +1030 Subject: [PATCH] re PR target/54009 (incorrect code generated for DFmode lo_sum mem) gcc/ PR target/54009 * config/rs6000/rs6000.c (mem_operand_gpr): Check that LO_SUM addresses won't wrap when offsetting. (rs6000_secondary_reload): Provide secondary reloads needed for wrapping LO_SUM addresses. gcc/testsuite/ PR target/54009 * gcc.target/powerpc/pr54009.c: New test. From-SVN: r195836 --- gcc/ChangeLog | 8 ++ gcc/config/rs6000/rs6000.c | 110 +++++++++++++-------- gcc/testsuite/ChangeLog | 2 + gcc/testsuite/gcc.target/powerpc/pr54009.c | 43 ++++++++ 4 files changed, 124 insertions(+), 39 deletions(-) create mode 100644 gcc/testsuite/gcc.target/powerpc/pr54009.c diff --git a/gcc/ChangeLog b/gcc/ChangeLog index 24d2587588a..148aa675c8a 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,11 @@ +2013-02-07 Alan Modra + + PR target/54009 + * config/rs6000/rs6000.c (mem_operand_gpr): Check that LO_SUM + addresses won't wrap when offsetting. + (rs6000_secondary_reload): Provide secondary reloads needed for + wrapping LO_SUM addresses. + 2013-02-06 Thomas Schwinge * config/gnu.h (GNU_USER_TARGET_OS_CPP_BUILTINS): Never define diff --git a/gcc/config/rs6000/rs6000.c b/gcc/config/rs6000/rs6000.c index 76d7f3da613..8b5f030c551 100644 --- a/gcc/config/rs6000/rs6000.c +++ b/gcc/config/rs6000/rs6000.c @@ -5135,17 +5135,14 @@ mem_operand_gpr (rtx op, enum machine_mode mode) if (TARGET_POWERPC64 && (offset & 3) != 0) return false; - if (GET_CODE (addr) == LO_SUM) - /* We know by alignment that ABI_AIX medium/large model toc refs - will not cross a 32k boundary, since all entries in the - constant pool are naturally aligned and we check alignment for - other medium model toc-relative addresses. For ABI_V4 and - ABI_DARWIN lo_sum addresses, we just check that 64-bit - offsets are 4-byte aligned. */ - return true; - extra = GET_MODE_SIZE (mode) - UNITS_PER_WORD; gcc_assert (extra >= 0); + + if (GET_CODE (addr) == LO_SUM) + /* For lo_sum addresses, we must allow any offset except one that + causes a wrap, so test only the low 16 bits. */ + offset = ((offset & 0xffff) ^ 0x8000) - 0x8000; + return offset + 0x8000 < 0x10000u - extra; } @@ -13823,19 +13820,36 @@ rs6000_secondary_reload (bool in_p, && MEM_P (x) && GET_MODE_SIZE (GET_MODE (x)) >= UNITS_PER_WORD) { - rtx off = address_offset (XEXP (x, 0)); - unsigned int extra = GET_MODE_SIZE (GET_MODE (x)) - UNITS_PER_WORD; - - if (off != NULL_RTX - && (INTVAL (off) & 3) != 0 - && (unsigned HOST_WIDE_INT) INTVAL (off) + 0x8000 < 0x10000 - extra) - { - if (in_p) - sri->icode = CODE_FOR_reload_di_load; + rtx addr = XEXP (x, 0); + rtx off = address_offset (addr); + + if (off != NULL_RTX) + { + unsigned int extra = GET_MODE_SIZE (GET_MODE (x)) - UNITS_PER_WORD; + unsigned HOST_WIDE_INT offset = INTVAL (off); + + /* We need a secondary reload when our legitimate_address_p + says the address is good (as otherwise the entire address + will be reloaded), and the offset is not a multiple of + four or we have an address wrap. Address wrap will only + occur for LO_SUMs since legitimate_offset_address_p + rejects addresses for 16-byte mems that will wrap. */ + if (GET_CODE (addr) == LO_SUM + ? (1 /* legitimate_address_p allows any offset for lo_sum */ + && ((offset & 3) != 0 + || ((offset & 0xffff) ^ 0x8000) >= 0x10000 - extra)) + : (offset + 0x8000 < 0x10000 - extra /* legitimate_address_p */ + && (offset & 3) != 0)) + { + if (in_p) + sri->icode = CODE_FOR_reload_di_load; + else + sri->icode = CODE_FOR_reload_di_store; + sri->extra_cost = 2; + ret = NO_REGS; + } else - sri->icode = CODE_FOR_reload_di_store; - sri->extra_cost = 2; - ret = NO_REGS; + default_p = true; } else default_p = true; @@ -13845,25 +13859,43 @@ rs6000_secondary_reload (bool in_p, && MEM_P (x) && GET_MODE_SIZE (GET_MODE (x)) > UNITS_PER_WORD) { - rtx off = address_offset (XEXP (x, 0)); - unsigned int extra = GET_MODE_SIZE (GET_MODE (x)) - UNITS_PER_WORD; - - /* We need a secondary reload only when our legitimate_address_p - says the address is good (as otherwise the entire address - will be reloaded). So for mode sizes of 8 and 16 this will - be when the offset is in the ranges [0x7ffc,0x7fff] and - [0x7ff4,0x7ff7] respectively. Note that the address we see - here may have been manipulated by legitimize_reload_address. */ - if (off != NULL_RTX - && ((unsigned HOST_WIDE_INT) INTVAL (off) - (0x8000 - extra) - < UNITS_PER_WORD)) - { - if (in_p) - sri->icode = CODE_FOR_reload_si_load; + rtx addr = XEXP (x, 0); + rtx off = address_offset (addr); + + if (off != NULL_RTX) + { + unsigned int extra = GET_MODE_SIZE (GET_MODE (x)) - UNITS_PER_WORD; + unsigned HOST_WIDE_INT offset = INTVAL (off); + + /* We need a secondary reload when our legitimate_address_p + says the address is good (as otherwise the entire address + will be reloaded), and we have a wrap. + + legitimate_lo_sum_address_p allows LO_SUM addresses to + have any offset so test for wrap in the low 16 bits. + + legitimate_offset_address_p checks for the range + [-0x8000,0x7fff] for mode size of 8 and [-0x8000,0x7ff7] + for mode size of 16. We wrap at [0x7ffc,0x7fff] and + [0x7ff4,0x7fff] respectively, so test for the + intersection of these ranges, [0x7ffc,0x7fff] and + [0x7ff4,0x7ff7] respectively. + + Note that the address we see here may have been + manipulated by legitimize_reload_address. */ + if (GET_CODE (addr) == LO_SUM + ? ((offset & 0xffff) ^ 0x8000) >= 0x10000 - extra + : offset - (0x8000 - extra) < UNITS_PER_WORD) + { + if (in_p) + sri->icode = CODE_FOR_reload_si_load; + else + sri->icode = CODE_FOR_reload_si_store; + sri->extra_cost = 2; + ret = NO_REGS; + } else - sri->icode = CODE_FOR_reload_si_store; - sri->extra_cost = 2; - ret = NO_REGS; + default_p = true; } else default_p = true; diff --git a/gcc/testsuite/ChangeLog b/gcc/testsuite/ChangeLog index 7dc9a025de6..d3407fdcbf8 100644 --- a/gcc/testsuite/ChangeLog +++ b/gcc/testsuite/ChangeLog @@ -1,5 +1,7 @@ 2013-02-07 Alan Modra + PR target/54009 + * gcc.target/powerpc/pr54009.c: New test. PR target/54131 * gfortran.dg/pr54131.f: New test. diff --git a/gcc/testsuite/gcc.target/powerpc/pr54009.c b/gcc/testsuite/gcc.target/powerpc/pr54009.c new file mode 100644 index 00000000000..9af98ab6c8b --- /dev/null +++ b/gcc/testsuite/gcc.target/powerpc/pr54009.c @@ -0,0 +1,43 @@ +/* { dg-do compile { target { powerpc*-*-* } } } */ +/* { dg-require-effective-target powerpc_fprs } */ +/* { dg-options "-O2" } */ +/* { dg-final { scan-assembler-not "\[\+\]32768" } } */ + +/* -O2 -m32 store to x.d in w was + lis 9,x+32764@ha + stw 10,x+32764@l(9) + stw 11,x+32768@l(9) <-- wrap! */ + +struct big { + char a[32764]; + double d __attribute__ ((aligned (4))); +} __attribute__ ((packed)); + +extern struct big x; +double y; + +void r (void) +{ + double tmp = x.d; +#if 1 + asm ("#": "+r" (tmp) + : : "fr0", "fr1", "fr2", "fr3", "fr4", "fr5", "fr6", "fr7", + "fr8", "fr9", "fr10", "fr11", "fr12", "fr13", "fr14", "fr15", + "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", "fr22", "fr23", + "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", "fr30", "fr31"); +#endif + y = tmp; +} + +void w (void) +{ + double tmp = y; +#if 1 + asm ("#": "+r" (tmp) + : : "fr0", "fr1", "fr2", "fr3", "fr4", "fr5", "fr6", "fr7", + "fr8", "fr9", "fr10", "fr11", "fr12", "fr13", "fr14", "fr15", + "fr16", "fr17", "fr18", "fr19", "fr20", "fr21", "fr22", "fr23", + "fr24", "fr25", "fr26", "fr27", "fr28", "fr29", "fr30", "fr31"); +#endif + x.d = tmp; +} -- 2.30.2