This fixes the bug reported in PR 89411 where we accidentally generated a %lo with an offset that overflowed at link time if the symbol was allocated to an address just below the 2KB limit for %lo. This solves the problem by checking for BLKmode references, pulling out the SYMBOL_REF_DECL, and then getting size and alignment from the decl instead of from the mode.
Tested with 32-bit/64-bit elf/linux cross builds and checks. There were no regressions. The new testcase fails without the patch and works with the patch. Committed. Jim gcc/ PR target/89411 * config/riscv/riscv.c (riscv_valid_lo_sum_p): New arg x. New locals align, size, offset. Use them to handle a BLKmode reference. Update comment. (riscv_classify_address): Pass info->offset to riscv_valid_lo_sum_p. gcc/testsuite/ PR target/89411 * gcc.target/riscv/losum-overflow.c: New test. --- gcc/config/riscv/riscv.c | 43 ++++++++++++++++--- .../gcc.target/riscv/losum-overflow.c | 29 +++++++++++++ 2 files changed, 66 insertions(+), 6 deletions(-) create mode 100644 gcc/testsuite/gcc.target/riscv/losum-overflow.c diff --git a/gcc/config/riscv/riscv.c b/gcc/config/riscv/riscv.c index 8e78ab76375..d8446f82b96 100644 --- a/gcc/config/riscv/riscv.c +++ b/gcc/config/riscv/riscv.c @@ -708,11 +708,15 @@ riscv_split_symbol_type (enum riscv_symbol_type symbol_type) } /* Return true if a LO_SUM can address a value of mode MODE when the - LO_SUM symbol has type SYM_TYPE. */ + LO_SUM symbol has type SYM_TYPE. X is the LO_SUM second operand, which + is used when the mode is BLKmode. */ static bool -riscv_valid_lo_sum_p (enum riscv_symbol_type sym_type, machine_mode mode) +riscv_valid_lo_sum_p (enum riscv_symbol_type sym_type, machine_mode mode, + rtx x) { + int align, size; + /* Check that symbols of type SYMBOL_TYPE can be used to access values of mode MODE. */ if (riscv_symbol_insns (sym_type) == 0) @@ -722,11 +726,38 @@ riscv_valid_lo_sum_p (enum riscv_symbol_type sym_type, machine_mode mode) if (!riscv_split_symbol_type (sym_type)) return false; + /* We can't tell size or alignment when we have BLKmode, so try extracing a + decl from the symbol if possible. */ + if (mode == BLKmode) + { + rtx offset; + + /* Extract the symbol from the LO_SUM operand, if any. */ + split_const (x, &x, &offset); + + /* Might be a CODE_LABEL. We can compute align but not size for that, + so don't bother trying to handle it. */ + if (!SYMBOL_REF_P (x)) + return false; + + /* Use worst case assumptions if we don't have a SYMBOL_REF_DECL. */ + align = (SYMBOL_REF_DECL (x) + ? DECL_ALIGN (SYMBOL_REF_DECL (x)) + : 1); + size = (SYMBOL_REF_DECL (x) && DECL_SIZE (SYMBOL_REF_DECL (x)) + ? tree_to_uhwi (DECL_SIZE (SYMBOL_REF_DECL (x))) + : 2*BITS_PER_WORD); + } + else + { + align = GET_MODE_ALIGNMENT (mode); + size = GET_MODE_BITSIZE (mode); + } + /* We may need to split multiword moves, so make sure that each word can be accessed without inducing a carry. */ - if (GET_MODE_SIZE (mode) > UNITS_PER_WORD - && (!TARGET_STRICT_ALIGN - || GET_MODE_BITSIZE (mode) > GET_MODE_ALIGNMENT (mode))) + if (size > BITS_PER_WORD + && (!TARGET_STRICT_ALIGN || size > align)) return false; return true; @@ -772,7 +803,7 @@ riscv_classify_address (struct riscv_address_info *info, rtx x, info->symbol_type = riscv_classify_symbolic_expression (info->offset); return (riscv_valid_base_register_p (info->reg, mode, strict_p) - && riscv_valid_lo_sum_p (info->symbol_type, mode)); + && riscv_valid_lo_sum_p (info->symbol_type, mode, info->offset)); case CONST_INT: /* Small-integer addresses don't occur very often, but they diff --git a/gcc/testsuite/gcc.target/riscv/losum-overflow.c b/gcc/testsuite/gcc.target/riscv/losum-overflow.c new file mode 100644 index 00000000000..9c01c7feb54 --- /dev/null +++ b/gcc/testsuite/gcc.target/riscv/losum-overflow.c @@ -0,0 +1,29 @@ +/* { dg-do compile } */ +/* { dg-options "-march=rv32gc -mabi=ilp32 -O2 -fno-section-anchors" } */ + +/* Check for %lo overflow. Adding an offset larger than the alignment can + overflow if the data is allocated to an address mod 4KB that is between + 2KB-offset+1 and 2KB-1. */ +typedef long long int int64_t; + +#pragma pack(push) +#pragma pack(1) +struct S0 { + signed f0 : 4; + const volatile int64_t f1; + volatile signed f2 : 1; + signed f3 : 31; + unsigned f4 : 8; + signed f5 : 20; + unsigned f6 : 5; +}; +#pragma pack(pop) + +struct S0 g_3030 = {0,-9L,-0,-22553,7,-841,1}; + +int64_t +sub (void) +{ + return g_3030.f1; +} +/* { dg-final { scan-assembler-not "%lo\\(g_3030\\+4\\)" } } */ -- 2.17.1