The following testcase taken from the linux kernel is miscompiled on powerpc64-linux.
/* -m64 -mcmodel=medium -O -S -fno-section-anchors */ static int x; unsigned long foo (void) { return ((unsigned long) &x) - 0xc000000000000000; } generates addis 3,2,x+4611686018427387904@toc@ha addi 3,3,x+4611686018427387904@toc@l blr losing the top 32 bits of the offset. Sadly, the assembler and linker do not complain, which is a hole in the ABI. (@ha and _HA relocs as per the ABI won't complain about overflow since they might be used in a @highesta, @highera sequence loading a 64-bit value.) This patch stops combine merging large offsets into a symbol addend by copying code from reg_or_add_cint_operand to a new predicate, add_cint_operand, and using that to restrict the range of offsets. Bootstrapped and regression tested powerpc64-linux. OK to apply? * config/rs6000/predicates.md (add_cint_operand): New. * config/rs6000/rs6000.md (largetoc_high_plus): Restrict offset using add_cint_operand. (largetoc_high_plus_aix): Likewise. Index: gcc/config/rs6000/predicates.md =================================================================== --- gcc/config/rs6000/predicates.md (revision 202264) +++ gcc/config/rs6000/predicates.md (working copy) @@ -376,6 +376,12 @@ (ior (match_code "const_int") (match_operand 0 "gpc_reg_operand"))) +;; Return 1 if op is a constant integer valid for addition with addis, addi. +(define_predicate "add_cint_operand" + (and (match_code "const_int") + (match_test "(unsigned HOST_WIDE_INT) (INTVAL (op) + 0x80008000) + < (unsigned HOST_WIDE_INT) 0x100000000ll"))) + ;; Return 1 if op is a constant integer valid for addition ;; or non-special register. (define_predicate "reg_or_add_cint_operand" Index: gcc/config/rs6000/rs6000.md =================================================================== --- gcc/config/rs6000/rs6000.md (revision 202264) +++ gcc/config/rs6000/rs6000.md (working copy) @@ -12207,7 +12209,7 @@ (unspec [(match_operand:DI 1 "" "") (match_operand:DI 2 "gpc_reg_operand" "b")] UNSPEC_TOCREL) - (match_operand 3 "const_int_operand" "n"))))] + (match_operand 3 "add_cint_operand" "n"))))] "TARGET_ELF && TARGET_CMODEL != CMODEL_SMALL" "addis %0,%2,%1+%3@toc@ha") @@ -12218,7 +12220,7 @@ (unspec [(match_operand:P 1 "" "") (match_operand:P 2 "gpc_reg_operand" "b")] UNSPEC_TOCREL) - (match_operand 3 "const_int_operand" "n"))))] + (match_operand 3 "add_cint_operand" "n"))))] "TARGET_XCOFF && TARGET_CMODEL != CMODEL_SMALL" "addis %0,%1+%3@u(%2)") -- Alan Modra Australia Development Lab, IBM