The GCC optimizer can generate symbols with non-zero offset from simple if-statements. Bit zero is used for the Arm/Thumb state bit, so relocations with offsets fail if it changes bit zero and the relocation forces bit zero to true. The fix is to disable offsets on function pointer symbols.
ARMv5te bootstrap OK, regression tests pass. OK for commit? ChangeLog: 2019-02-06 Wilco Dijkstra <wdijk...@arm.com> gcc/ PR target/89222 * config/arm/arm.md (movsi): Use arm_cannot_force_const_mem to decide when to split off an offset from a symbol. * config/arm/arm.c (arm_cannot_force_const_mem): Disallow offsets in function symbols. * config/arm/arm-protos.h (arm_cannot_force_const_mem): Add. testsuite/ PR target/89222 * gcc.target/arm/pr89222.c: Add new test. -- diff --git a/gcc/config/arm/arm-protos.h b/gcc/config/arm/arm-protos.h index 79ede0db174fcce87abe8b4d18893550d4c7e2f6..0bedbe5110853617ecf7456bbaa56b1405fb65dd 100644 --- a/gcc/config/arm/arm-protos.h +++ b/gcc/config/arm/arm-protos.h @@ -184,6 +184,7 @@ extern void arm_init_cumulative_args (CUMULATIVE_ARGS *, tree, rtx, tree); extern bool arm_pad_reg_upward (machine_mode, tree, int); #endif extern int arm_apply_result_size (void); +extern bool arm_cannot_force_const_mem (machine_mode, rtx); #endif /* RTX_CODE */ diff --git a/gcc/config/arm/arm.c b/gcc/config/arm/arm.c index c4c9b4a667100d81d918196713e40b01ee232ee2..ccd4211045066d8edb89dd4c23d554517639f8f6 100644 --- a/gcc/config/arm/arm.c +++ b/gcc/config/arm/arm.c @@ -178,7 +178,6 @@ static void arm_internal_label (FILE *, const char *, unsigned long); static void arm_output_mi_thunk (FILE *, tree, HOST_WIDE_INT, HOST_WIDE_INT, tree); static bool arm_have_conditional_execution (void); -static bool arm_cannot_force_const_mem (machine_mode, rtx); static bool arm_legitimate_constant_p (machine_mode, rtx); static bool arm_rtx_costs (rtx, machine_mode, int, int, int *, bool); static int arm_address_cost (rtx, machine_mode, addr_space_t, bool); @@ -8936,15 +8935,20 @@ arm_legitimate_constant_p (machine_mode mode, rtx x) /* Implement TARGET_CANNOT_FORCE_CONST_MEM. */ -static bool +bool arm_cannot_force_const_mem (machine_mode mode ATTRIBUTE_UNUSED, rtx x) { rtx base, offset; + split_const (x, &base, &offset); - if (ARM_OFFSETS_MUST_BE_WITHIN_SECTIONS_P) + if (GET_CODE (base) == SYMBOL_REF) { - split_const (x, &base, &offset); - if (GET_CODE (base) == SYMBOL_REF + /* Function symbols cannot have an offset due to the Thumb bit. */ + if ((SYMBOL_REF_FLAGS (base) & SYMBOL_FLAG_FUNCTION) + && INTVAL (offset) != 0) + return true; + + if (ARM_OFFSETS_MUST_BE_WITHIN_SECTIONS_P && !offset_within_block_p (base, INTVAL (offset))) return true; } diff --git a/gcc/config/arm/arm.md b/gcc/config/arm/arm.md index aa759624f8f617576773aa75fd6239d6e06e8a13..00fccd964a86dd814f15e4a1fdf5b47173a3ee3f 100644 --- a/gcc/config/arm/arm.md +++ b/gcc/config/arm/arm.md @@ -5981,17 +5981,13 @@ (define_expand "movsi" } } - if (ARM_OFFSETS_MUST_BE_WITHIN_SECTIONS_P) + if (arm_cannot_force_const_mem (SImode, operands[1])) { split_const (operands[1], &base, &offset); - if (GET_CODE (base) == SYMBOL_REF - && !offset_within_block_p (base, INTVAL (offset))) - { - tmp = can_create_pseudo_p () ? gen_reg_rtx (SImode) : operands[0]; - emit_move_insn (tmp, base); - emit_insn (gen_addsi3 (operands[0], tmp, offset)); - DONE; - } + tmp = can_create_pseudo_p () ? gen_reg_rtx (SImode) : operands[0]; + emit_move_insn (tmp, base); + emit_insn (gen_addsi3 (operands[0], tmp, offset)); + DONE; } /* Recognize the case where operand[1] is a reference to thread-local diff --git a/gcc/testsuite/gcc.target/arm/pr89222.c b/gcc/testsuite/gcc.target/arm/pr89222.c new file mode 100644 index 0000000000000000000000000000000000000000..d26d7df17544db8426331e67b9a36d749ec6c6d1 --- /dev/null +++ b/gcc/testsuite/gcc.target/arm/pr89222.c @@ -0,0 +1,32 @@ +/* { dg-do compile } */ +/* { dg-options "-O2" } */ + +void g (void); + +void f1 (int x) +{ + if (x != (int) g + 3) + return; + g(); +} + +void (*a2)(void); + +void f2 (void) +{ + a2 = &g + 3; +} + +typedef void (*__sighandler_t)(int); +void handler (int); + +void f3 (int x) +{ + __sighandler_t h = &handler; + if (h != (__sighandler_t) 2 && h != (__sighandler_t) 1) + h (x); +} + +/* { dg-final { scan-assembler-times {add(?:s)?\tr[0-9]+, r[0-9]+, #3} 2 } } */ +/* { dg-final { scan-assembler-not {.word\tg\+3} } } */ +/* { dg-final { scan-assembler-not {.word\thandler-1} } } */