Hello, when loading a TDmode value into floating-point registers, they need to go into a register pair with the even register holding the most significant word and the odd register holding the least significant word, because this is what the instruction set expects. This hold for both big-endian and little-endian mode.
However, in little-endian mode, the usual subreg logic will place the least significant word into the lower-numbered register, which is wrong. This causes all _Decimal128 tests to fail when using a --with-cpu that includes DFP instructions. To fix this, the patch below: - disables subregs of TDmode in FPRs in little-endian mode in rs6000_cannot_change_mode_class, to ensure nobody is using the generic subreg logic for such values, and - changes rs6000_split_multireg_move to avoid using simplify_gen_subreg on such values, but instead create the correct register numbers "by hand" Note that this patch implies an ABI change on little-endian for passing _Decimal128 values in register pairs. Also note that this patch does not change how TDmode values are loaded into GPRs: on little-endian, this means we do get the usual LE subreg order there (least significant word in lowest-numbered register). This does still seem the right thing to do, because there are no instructions that operate on DFP values in GPRs, and because this allows us to keep using subregs on GPRs in that case, and it allows moving TDmode between memory and GPRs (e.g. for vararg arguments) in the usual way. Tested on powerpc64le-linux, fixes all remaining DFP-related test suite failures. OK for mainline? Bye, Ulrich ChangeLog: * config/rs6000/rs6000.c (rs6000_cannot_change_mode_class): Do not allow subregs of TDmode in FPRs of smaller size in little-endian. (rs6000_split_multireg_move): When splitting an access to TDmode in FPRs, do not use simplify_gen_subreg. Index: gcc/config/rs6000/rs6000.c =================================================================== --- gcc/config/rs6000/rs6000.c (revision 204948) +++ gcc/config/rs6000/rs6000.c (working copy) @@ -16664,6 +16664,13 @@ if (TARGET_IEEEQUAD && (to == TFmode || from == TFmode)) return true; + /* TDmode in floating-mode registers must always go into a register + pair with the most significant word in the even-numbered register + to match ISA requirements. In little-endian mode, this does not + match subreg numbering, so we cannot allow subregs. */ + if (!BYTES_BIG_ENDIAN && (to == TDmode || from == TDmode)) + return true; + if (from_size < 8 || to_size < 8) return true; @@ -19606,6 +19613,39 @@ gcc_assert (reg_mode_size * nregs == GET_MODE_SIZE (mode)); + /* TDmode residing in FP registers is special, since the ISA requires that + the lower-numbered word of a register pair is always the most significant + word, even in little-endian mode. This does not match the usual subreg + semantics, so we cannnot use simplify_gen_subreg in those cases. Access + the appropriate constituent registers "by hand" in little-endian mode. + + Note we do not need to check for destructive overlap here since TDmode + can only reside in even/odd register pairs. */ + if (FP_REGNO_P (reg) && DECIMAL_FLOAT_MODE_P (mode) && !BYTES_BIG_ENDIAN) + { + rtx p_src, p_dst; + int i; + + for (i = 0; i < nregs; i++) + { + if (REG_P (src) && FP_REGNO_P (REGNO (src))) + p_src = gen_rtx_REG (reg_mode, REGNO (src) + nregs - 1 - i); + else + p_src = simplify_gen_subreg (reg_mode, src, mode, + i * reg_mode_size); + + if (REG_P (dst) && FP_REGNO_P (REGNO (dst))) + p_dst = gen_rtx_REG (reg_mode, REGNO (dst) + nregs - 1 - i); + else + p_dst = simplify_gen_subreg (reg_mode, dst, mode, + i * reg_mode_size); + + emit_insn (gen_rtx_SET (VOIDmode, p_dst, p_src)); + } + + return; + } + if (REG_P (src) && REG_P (dst) && (REGNO (src) < REGNO (dst))) { /* Move register range backwards, if we might have destructive -- Dr. Ulrich Weigand GNU/Linux compilers and toolchain ulrich.weig...@de.ibm.com