[1/4] rs6000: Make all 128 bit scalar FP modes have 128 bit precision [PR112993]

Message ID 813e8433-0428-1854-c82c-fc0743368fa8@linux.ibm.com
State New
Headers
Series [1/4] rs6000: Make all 128 bit scalar FP modes have 128 bit precision [PR112993] |

Commit Message

Kewen.Lin May 8, 2024, 5:21 a.m. UTC
  Hi,

On rs6000, there are three 128 bit scalar floating point
modes TFmode, IFmode and KFmode.  With some historical
reasons, we defines them with different mode precisions,
that is KFmode 126, TFmode 127 and IFmode 128.  But in
fact all of them should have the same mode precision 128,
this special setting has caused some issues like some
unexpected failures mentioned in [1] and also made us have
to introduce some workarounds, such as: the workaround in
build_common_tree_nodes for KFmode 126, the workaround in
range_compatible_p for same mode but different precision
issue.

This patch is to make these three 128 bit scalar floating
point modes TFmode, IFmode and KFmode have 128 bit mode
precision, and keep the order same as previous in order
to make machine independent parts of the compiler not try
to widen IFmode to TFmode.  To make build_common_tree_nodes
be able to find the correct mode for long double type node,
it introduces one hook mode_for_longdouble to offer target
a way to specify the mode used for long double type node.
Previously I tried to put the order as TF/KF/IF then long
double type node can pick up the TF as expected, but we
need to teach some functions not to try the conversions
from IF(TF) to KF, one more important thing is that we
would further remove TF and leave only two modes for 128
bit floating point modes, without such hook the first 128
bit mode will be chosen for long double type node but
whichever we replace first would be possible not the
expected one as it actually depends on the selected long
double format.

In function convert_mode_scalar, it adopts sext_optab for
same precision modes conversion if !DECIMAL_FLOAT_MODE_P,
so we only need to support sext_optab for any possible
conversion.  Thus this patch removes some useless trunc
optab supports, supplements one new sext_optab which calls
the common handler rs6000_expand_float128_convert, unnames
two define_insn_and_split to avoid conflicts and make them
more clear.  Considering the current implementation that
there is no chance to have KF <-> IF conversion (since
either of them would be TF already), it adds two dummy
define_expands to assert this.

Bootstrapped and regress-tested (with patch 2/4) on:
  - powerpc64-linux-gnu P8/P9 (with ibm128 by default)
  - powerpc64le-linux-gnu P9/P10 (with ibm128 by default)
  - powerpc64le-linux-gnu P9 (with ieee128 by default)

Is it OK for trunk (especially the generic code change)?

[1] https://inbox.sourceware.org/gcc-patches/
    718677e7-614d-7977-312d-05a75e1fd5b4@linux.ibm.com/

BR,
Kewen
-----
	PR target/112993

gcc/ChangeLog:

	* config/rs6000/rs6000-modes.def (IFmode, KFmode, TFmode): Define
	with FLOAT_MODE instead of FRACTIONAL_FLOAT_MODE, don't use special
	precisions any more.
	(rs6000-modes.h): Remove include.
	* config/rs6000/rs6000-modes.h: Remove.
	* config/rs6000/rs6000.h (rs6000-modes.h): Remove include.
	* config/rs6000/t-rs6000: Remove rs6000-modes.h include.
	* config/rs6000/rs6000.cc (rs6000_option_override_internal): Replace
	all uses of FLOAT_PRECISION_TFmode with 128.
	(TARGET_C_MODE_FOR_LONGDOUBLE): New macro.
	(rs6000_c_mode_for_longdouble): New hook implementation.
	* config/rs6000/rs6000.md (define_expand trunciftf2): Remove.
	(define_expand truncifkf2): Remove.
	(define_expand trunckftf2): Remove.
	(define_expand trunctfif2): Remove.
	(define_expand expandtfkf2, expandtfif2): Merge to ...
        (define_expand expandtf<IFKF>2): ... this, new.
	(define_expand expandiftf2): Merge to ...
        (define_expand expand<IFKF>tf2): ... this, new.
	(define_expand expandiftf2): Update with assert.
	(define_expand expandkfif2): New.
	(define_insn_and_split extendkftf2): Rename to  ...
	(define_insn_and_split *extendkftf2): ... this.
	(define_insn_and_split trunctfkf2): Rename to ...
	(define_insn_and_split *extendtfkf2): ... this.
	* expr.cc (convert_mode_scalar): Allow same precision conversion
	between scalar floating point modes if whose underlying format is
	ibm_extended_format or ieee_quad_format, and refactor assertion
	with new lambda function acceptable_same_precision_modes.
	* doc/tm.texi: Regenerate.
	* doc/tm.texi.in (TARGET_C_MODE_FOR_LONGDOUBLE): New hook.
	* target.def (mode_for_longdouble): Likewise.
	* targhooks.cc (default_mode_for_longdouble): New hook default
	implementation.
	* targhooks.h (default_mode_for_longdouble): New hook declaration.
	* tree.cc (build_common_tree_nodes): Call newly added hook
	targetm.c.mode_for_longdouble.
---
 gcc/config/rs6000/rs6000-modes.def | 31 +++++--------
 gcc/config/rs6000/rs6000-modes.h   | 36 ---------------
 gcc/config/rs6000/rs6000.cc        | 18 +++++---
 gcc/config/rs6000/rs6000.h         |  5 ---
 gcc/config/rs6000/rs6000.md        | 72 ++++++++----------------------
 gcc/config/rs6000/t-rs6000         |  1 -
 gcc/doc/tm.texi                    |  6 +++
 gcc/doc/tm.texi.in                 |  2 +
 gcc/expr.cc                        | 27 ++++++++---
 gcc/target.def                     |  7 +++
 gcc/targhooks.cc                   |  9 ++++
 gcc/targhooks.h                    |  1 +
 gcc/tree.cc                        |  1 +
 13 files changed, 90 insertions(+), 126 deletions(-)
 delete mode 100644 gcc/config/rs6000/rs6000-modes.h

--
2.39.1
  

Comments

Joseph Myers May 9, 2024, 7:12 p.m. UTC | #1
On Wed, 8 May 2024, Kewen.Lin wrote:

> to widen IFmode to TFmode.  To make build_common_tree_nodes
> be able to find the correct mode for long double type node,
> it introduces one hook mode_for_longdouble to offer target
> a way to specify the mode used for long double type node.

I don't really like layering a hook on top of the old target macro as a 
way to address a deficiency in the design of that target macro (floating 
types should have their mode, not a poorly defined precision value, 
specified directly by the target).

A better hook design might be something like mode_for_floating_type (enum 
tree_index), where the argument is TI_FLOAT_TYPE, TI_DOUBLE_TYPE or 
TI_LONG_DOUBLE_TYPE, replacing all definitions and uses of 
FLOAT_TYPE_SIZE, DOUBLE_TYPE_SIZE and LONG_DOUBLE_TYPE_SIZE with the 
single new hook and appropriate definitions for each target (with a 
default definition that uses SFmode for float and DFmode for double and 
long double, which would be suitable for many targets).
  
Richard Biener May 10, 2024, 6:31 a.m. UTC | #2
On Thu, May 9, 2024 at 9:12 PM Joseph Myers <josmyers@redhat.com> wrote:
>
> On Wed, 8 May 2024, Kewen.Lin wrote:
>
> > to widen IFmode to TFmode.  To make build_common_tree_nodes
> > be able to find the correct mode for long double type node,
> > it introduces one hook mode_for_longdouble to offer target
> > a way to specify the mode used for long double type node.
>
> I don't really like layering a hook on top of the old target macro as a
> way to address a deficiency in the design of that target macro (floating
> types should have their mode, not a poorly defined precision value,
> specified directly by the target).

Seconded.

> A better hook design might be something like mode_for_floating_type (enum
> tree_index), where the argument is TI_FLOAT_TYPE, TI_DOUBLE_TYPE or
> TI_LONG_DOUBLE_TYPE, replacing all definitions and uses of
> FLOAT_TYPE_SIZE, DOUBLE_TYPE_SIZE and LONG_DOUBLE_TYPE_SIZE with the
> single new hook and appropriate definitions for each target (with a
> default definition that uses SFmode for float and DFmode for double and
> long double, which would be suitable for many targets).

In fact replacing all of X_TYPE_SIZE with a single hook might be worthwhile
though this removes the "convenient" defaulting, requiring each target to
enumerate all standard C ABI type modes.  But that might be also a good thing.

The most pragmatic solution would be to do
s/LONG_DOUBLE_TYPE_SIZE/LONG_DOUBLE_TYPE_MODE/

Richard.

> --
> Joseph S. Myers
> josmyers@redhat.com
>
  
Kewen.Lin May 13, 2024, 1:39 a.m. UTC | #3
Hi Joseph and Richi,

Thanks for the suggestions and comments!

on 2024/5/10 14:31, Richard Biener wrote:
> On Thu, May 9, 2024 at 9:12 PM Joseph Myers <josmyers@redhat.com> wrote:
>>
>> On Wed, 8 May 2024, Kewen.Lin wrote:
>>
>>> to widen IFmode to TFmode.  To make build_common_tree_nodes
>>> be able to find the correct mode for long double type node,
>>> it introduces one hook mode_for_longdouble to offer target
>>> a way to specify the mode used for long double type node.
>>
>> I don't really like layering a hook on top of the old target macro as a
>> way to address a deficiency in the design of that target macro (floating
>> types should have their mode, not a poorly defined precision value,
>> specified directly by the target).

Good point!

> 
> Seconded.
> 
>> A better hook design might be something like mode_for_floating_type (enum
>> tree_index), where the argument is TI_FLOAT_TYPE, TI_DOUBLE_TYPE or
>> TI_LONG_DOUBLE_TYPE, replacing all definitions and uses of
>> FLOAT_TYPE_SIZE, DOUBLE_TYPE_SIZE and LONG_DOUBLE_TYPE_SIZE with the
>> single new hook and appropriate definitions for each target (with a
>> default definition that uses SFmode for float and DFmode for double and
>> long double, which would be suitable for many targets).
> 

The originally proposed hook was meant to make the other ports unaffected,
but I agree that introducing such hook would be more clear.

> In fact replacing all of X_TYPE_SIZE with a single hook might be worthwhile
> though this removes the "convenient" defaulting, requiring each target to
> enumerate all standard C ABI type modes.  But that might be also a good thing.
> 

I guess the main value by extending from floating point types to all is to
unify them?  (Assuming that excepting for floating types the others would
not have multiple possible representations like what we faces on 128bit fp).

> The most pragmatic solution would be to do
> s/LONG_DOUBLE_TYPE_SIZE/LONG_DOUBLE_TYPE_MODE/

Yeah, this beats my proposed hook (assuming the default is VOIDmode too).

So it seems we have three alternatives here:
  1) s/LONG_DOUBLE_TYPE_SIZE/LONG_DOUBLE_TYPE_MODE/
  2) mode_for_floating_type
  3) mode_for_abi_type

Since 1) would make long double type special (different from the other types
having _TYPE_SIZE), personally I'm inclined to 3): implement 2) first, get
this patch series landed, extend to all.

Do you have any preference?  

BR,
Kewen
  
Richard Biener May 13, 2024, 7:11 a.m. UTC | #4
On Mon, May 13, 2024 at 3:39 AM Kewen.Lin <linkw@linux.ibm.com> wrote:
>
> Hi Joseph and Richi,
>
> Thanks for the suggestions and comments!
>
> on 2024/5/10 14:31, Richard Biener wrote:
> > On Thu, May 9, 2024 at 9:12 PM Joseph Myers <josmyers@redhat.com> wrote:
> >>
> >> On Wed, 8 May 2024, Kewen.Lin wrote:
> >>
> >>> to widen IFmode to TFmode.  To make build_common_tree_nodes
> >>> be able to find the correct mode for long double type node,
> >>> it introduces one hook mode_for_longdouble to offer target
> >>> a way to specify the mode used for long double type node.
> >>
> >> I don't really like layering a hook on top of the old target macro as a
> >> way to address a deficiency in the design of that target macro (floating
> >> types should have their mode, not a poorly defined precision value,
> >> specified directly by the target).
>
> Good point!
>
> >
> > Seconded.
> >
> >> A better hook design might be something like mode_for_floating_type (enum
> >> tree_index), where the argument is TI_FLOAT_TYPE, TI_DOUBLE_TYPE or
> >> TI_LONG_DOUBLE_TYPE, replacing all definitions and uses of
> >> FLOAT_TYPE_SIZE, DOUBLE_TYPE_SIZE and LONG_DOUBLE_TYPE_SIZE with the
> >> single new hook and appropriate definitions for each target (with a
> >> default definition that uses SFmode for float and DFmode for double and
> >> long double, which would be suitable for many targets).
> >
>
> The originally proposed hook was meant to make the other ports unaffected,
> but I agree that introducing such hook would be more clear.
>
> > In fact replacing all of X_TYPE_SIZE with a single hook might be worthwhile
> > though this removes the "convenient" defaulting, requiring each target to
> > enumerate all standard C ABI type modes.  But that might be also a good thing.
> >
>
> I guess the main value by extending from floating point types to all is to
> unify them?  (Assuming that excepting for floating types the others would
> not have multiple possible representations like what we faces on 128bit fp).
>
> > The most pragmatic solution would be to do
> > s/LONG_DOUBLE_TYPE_SIZE/LONG_DOUBLE_TYPE_MODE/
>
> Yeah, this beats my proposed hook (assuming the default is VOIDmode too).
>
> So it seems we have three alternatives here:
>   1) s/LONG_DOUBLE_TYPE_SIZE/LONG_DOUBLE_TYPE_MODE/
>   2) mode_for_floating_type
>   3) mode_for_abi_type
>
> Since 1) would make long double type special (different from the other types
> having _TYPE_SIZE), personally I'm inclined to 3): implement 2) first, get
> this patch series landed, extend to all.
>
> Do you have any preference?

Maybe do 3) but have the default hook implementation look at
*_TYPE_SIZE when the target doesn't implement the hook?  That would
force you to transition rs6000 away from *_TYPE_SIZE completely
but this would also prove the design.

Btw, for .c.mode_for_abi_type I'd exclude ADA_LONG_TYPE_SIZE.

Joseph, do you agree with this?  I'd not touch the target macros like
PTRDIFF_TYPE (those evaluating to a string) at this point though
they could be handled with a common target hook as well (not sure
if we'd want to have a unified hook for both?).

Thanks,
Richard.

>
> BR,
> Kewen
  
Joseph Myers May 13, 2024, 1:18 p.m. UTC | #5
On Mon, 13 May 2024, Kewen.Lin wrote:

> > In fact replacing all of X_TYPE_SIZE with a single hook might be worthwhile
> > though this removes the "convenient" defaulting, requiring each target to
> > enumerate all standard C ABI type modes.  But that might be also a good thing.
> > 
> 
> I guess the main value by extending from floating point types to all is to
> unify them?  (Assuming that excepting for floating types the others would
> not have multiple possible representations like what we faces on 128bit fp).

For integer types, giving the number of bits makes sense as an interface - 
there isn't an issue with different modes.

So I think it's appropriate for floating and integer types to have 
separate hooks - with the one for floating types returning a mode, and the 
one for integer types returning a number of bits.  (And also keep the 
existing separate hook for _FloatN / _FloatNx modes.)

That may also make for more convenient defaults (whether a target has long 
double wider than double is largely independent of what sizes it uses for 
integer types).
  

Patch

diff --git a/gcc/config/rs6000/rs6000-modes.def b/gcc/config/rs6000/rs6000-modes.def
index 094b246c834..b69593c40a6 100644
--- a/gcc/config/rs6000/rs6000-modes.def
+++ b/gcc/config/rs6000/rs6000-modes.def
@@ -18,12 +18,11 @@ 
    along with GCC; see the file COPYING3.  If not see
    <http://www.gnu.org/licenses/>.  */

-/* We order the 3 128-bit floating point types so that IFmode (IBM 128-bit
-   floating point) is the 128-bit floating point type with the highest
-   precision (128 bits).  This so that machine independent parts of the
-   compiler do not try to widen IFmode to TFmode on ISA 3.0 (power9) that has
-   hardware support for IEEE 128-bit.  We set TFmode (long double mode) in
-   between, and KFmode (explicit __float128) below it.
+/* We order the 3 128-bit floating point type modes here as KFmode, TFmode and
+   IFmode, it is the same as the previous order, to make machine independent
+   parts of the compiler do not try to widen IFmode to TFmode on ISA 3.0
+   (power9) that has hardware support for IEEE 128-bit.  We set TFmode (long
+   double mode) in between, and KFmode (explicit __float128) below it.

    Previously, IFmode and KFmode were defined to be fractional modes and TFmode
    was the standard mode.  Since IFmode does not define the normal arithmetic
@@ -33,24 +32,18 @@ 
    128-bit is not strictly a super-set of IBM extended double and the
    conversion to/from IEEE 128-bit was a function call.

-   We now make IFmode the highest fractional mode, which means its values are
-   not considered for widening.  Since we don't define insns for IFmode, the
-   IEEE 128-bit modes would not widen to IFmode.  */
-
-#ifndef RS6000_MODES_H
-#include "config/rs6000/rs6000-modes.h"
-#endif
-
-/* IBM 128-bit floating point.  */
-FRACTIONAL_FLOAT_MODE (IF, FLOAT_PRECISION_IFmode, 16, ibm_extended_format);
+   We now place IFmode last, which means its values are not considered for
+   widening.  Since we don't define insns for IFmode, the IEEE 128-bit modes
+   would not widen to IFmode.  */

 /* Explicit IEEE 128-bit floating point.  */
-FRACTIONAL_FLOAT_MODE (KF, FLOAT_PRECISION_KFmode, 16, ieee_quad_format);
-
+FLOAT_MODE (KF, 16, ieee_quad_format);
 /* 128-bit floating point, either IBM 128-bit or IEEE 128-bit.  This is
    adjusted in rs6000_option_override_internal to be the appropriate floating
    point type.  */
-FRACTIONAL_FLOAT_MODE (TF, FLOAT_PRECISION_TFmode, 16, ieee_quad_format);
+FLOAT_MODE (TF, 16, ieee_quad_format);
+/* IBM 128-bit floating point.  */
+FLOAT_MODE (IF, 16, ibm_extended_format);

 /* Add any extra modes needed to represent the condition code.

diff --git a/gcc/config/rs6000/rs6000-modes.h b/gcc/config/rs6000/rs6000-modes.h
deleted file mode 100644
index cf95330c265..00000000000
--- a/gcc/config/rs6000/rs6000-modes.h
+++ /dev/null
@@ -1,36 +0,0 @@ 
-/* Definitions 128-bit floating point precisions used by PowerPC.
-   Copyright (C) 2018-2024 Free Software Foundation, Inc.
-   Contributed by Michael Meissner (meissner@linux.ibm.com)
-
-   This file is part of GCC.
-
-   GCC is free software; you can redistribute it and/or modify it
-   under the terms of the GNU General Public License as published
-   by the Free Software Foundation; either version 3, or (at your
-   option) any later version.
-
-   GCC is distributed in the hope that it will be useful, but WITHOUT
-   ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
-   or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public
-   License for more details.
-
-   You should have received a copy of the GNU General Public License
-   along with GCC; see the file COPYING3.  If not see
-   <http://www.gnu.org/licenses/>.  */
-
-/* We order the 3 128-bit floating point types so that IFmode (IBM 128-bit
-   floating point) is the 128-bit floating point type with the highest
-   precision (128 bits).  This so that machine independent parts of the
-   compiler do not try to widen IFmode to TFmode on ISA 3.0 (power9) that has
-   hardware support for IEEE 128-bit.  We set TFmode (long double mode) in
-   between, and KFmode (explicit __float128) below it.
-
-   We won't encounter conversion from IEEE 128-bit to IBM 128-bit because we
-   don't have insns to support the IBM 128-bit aritmetic operations.  */
-
-#ifndef RS6000_MODES_H
-#define RS6000_MODES_H		1
-#define FLOAT_PRECISION_IFmode	128
-#define FLOAT_PRECISION_TFmode	127
-#define FLOAT_PRECISION_KFmode	126
-#endif	/* RS6000_MODES_H */
diff --git a/gcc/config/rs6000/rs6000.cc b/gcc/config/rs6000/rs6000.cc
index 6ba9df4f02e..518edf5d42a 100644
--- a/gcc/config/rs6000/rs6000.cc
+++ b/gcc/config/rs6000/rs6000.cc
@@ -1707,6 +1707,9 @@  static const scoped_attribute_specs *const rs6000_attribute_table[] =
 #undef TARGET_C_MODE_FOR_SUFFIX
 #define TARGET_C_MODE_FOR_SUFFIX rs6000_c_mode_for_suffix

+#undef TARGET_C_MODE_FOR_LONGDOUBLE
+#define TARGET_C_MODE_FOR_LONGDOUBLE rs6000_c_mode_for_longdouble
+
 #undef TARGET_INVALID_BINARY_OP
 #define TARGET_INVALID_BINARY_OP rs6000_invalid_binary_op

@@ -4093,7 +4096,7 @@  rs6000_option_override_internal (bool global_init_p)
      128 into the precision used for TFmode.  */
   int default_long_double_size = (RS6000_DEFAULT_LONG_DOUBLE_SIZE == 64
 				  ? 64
-				  : FLOAT_PRECISION_TFmode);
+				  : 128);

   /* Set long double size before the IEEE 128-bit tests.  */
   if (!OPTION_SET_P (rs6000_long_double_type_size))
@@ -4105,10 +4108,6 @@  rs6000_option_override_internal (bool global_init_p)
       else
 	rs6000_long_double_type_size = default_long_double_size;
     }
-  else if (rs6000_long_double_type_size == FLOAT_PRECISION_TFmode)
-    ; /* The option value can be seen when cl_target_option_restore is called.  */
-  else if (rs6000_long_double_type_size == 128)
-    rs6000_long_double_type_size = FLOAT_PRECISION_TFmode;

   /* Set -mabi=ieeelongdouble on some old targets.  In the future, power server
      systems will also set long double to be IEEE 128-bit.  AIX and Darwin
@@ -24370,6 +24369,15 @@  rs6000_c_mode_for_suffix (char suffix)
   return VOIDmode;
 }

+/* Target hook for c_mode_for_longdouble.  */
+static machine_mode
+rs6000_c_mode_for_longdouble ()
+{
+  if (TARGET_LONG_DOUBLE_128)
+    return TFmode;
+  return VOIDmode;
+}
+
 /* Target hook for invalid_arg_for_unprototyped_fn. */
 static const char *
 invalid_arg_for_unprototyped_fn (const_tree typelist, const_tree funcdecl, const_tree val)
diff --git a/gcc/config/rs6000/rs6000.h b/gcc/config/rs6000/rs6000.h
index 68bc45d65ba..ec7e26e165c 100644
--- a/gcc/config/rs6000/rs6000.h
+++ b/gcc/config/rs6000/rs6000.h
@@ -30,11 +30,6 @@ 
 #include "config/rs6000/rs6000-opts.h"
 #endif

-/* 128-bit floating point precision values.  */
-#ifndef RS6000_MODES_H
-#include "config/rs6000/rs6000-modes.h"
-#endif
-
 /* Definitions for the object file format.  These are set at
    compile-time.  */

diff --git a/gcc/config/rs6000/rs6000.md b/gcc/config/rs6000/rs6000.md
index bc8bc6ab060..91b323f15f8 100644
--- a/gcc/config/rs6000/rs6000.md
+++ b/gcc/config/rs6000/rs6000.md
@@ -9337,79 +9337,43 @@  (define_insn "*ieee_128bit_vsx_nabs<mode>2_internal"
   "xxlor %x0,%x1,%x2"
   [(set_attr "type" "veclogical")])

-;; Float128 conversion functions.  These expand to library function calls.
-;; We use expand to convert from IBM double double to IEEE 128-bit
-;; and trunc for the opposite.
-(define_expand "extendiftf2"
+;; Float128 conversion functions.
+;; If the conversion happens between IBM double double and
+;; IEEE 128-bit, it expands to library function calls.
+;; Generic code adopts sext_optab for same precision scalar
+;; float modes conversion, so we just need to define extend.
+(define_expand "extend<mode>tf2"
   [(set (match_operand:TF 0 "gpc_reg_operand")
-	(float_extend:TF (match_operand:IF 1 "gpc_reg_operand")))]
+	(float_extend:TF (match_operand:IFKF 1 "gpc_reg_operand")))]
   "TARGET_FLOAT128_TYPE"
 {
   rs6000_expand_float128_convert (operands[0], operands[1], false);
   DONE;
 })

-(define_expand "extendifkf2"
-  [(set (match_operand:KF 0 "gpc_reg_operand")
-	(float_extend:KF (match_operand:IF 1 "gpc_reg_operand")))]
-  "TARGET_FLOAT128_TYPE"
-{
-  rs6000_expand_float128_convert (operands[0], operands[1], false);
-  DONE;
-})
-
-(define_expand "extendtfkf2"
-  [(set (match_operand:KF 0 "gpc_reg_operand")
-	(float_extend:KF (match_operand:TF 1 "gpc_reg_operand")))]
+(define_expand "extendtf<mode>2"
+  [(set (match_operand:IFKF 0 "gpc_reg_operand")
+	(float_extend:IFKF (match_operand:TF 1 "gpc_reg_operand")))]
   "TARGET_FLOAT128_TYPE"
 {
   rs6000_expand_float128_convert (operands[0], operands[1], false);
   DONE;
 })

-(define_expand "extendtfif2"
-  [(set (match_operand:IF 0 "gpc_reg_operand")
-	(float_extend:IF (match_operand:TF 1 "gpc_reg_operand")))]
-  "TARGET_FLOAT128_TYPE"
-{
-  rs6000_expand_float128_convert (operands[0], operands[1], false);
-  DONE;
-})
-
-(define_expand "trunciftf2"
-  [(set (match_operand:TF 0 "gpc_reg_operand")
-	(float_truncate:TF (match_operand:IF 1 "gpc_reg_operand")))]
-  "TARGET_FLOAT128_TYPE"
-{
-  rs6000_expand_float128_convert (operands[0], operands[1], false);
-  DONE;
-})
-
-(define_expand "truncifkf2"
+(define_expand "extendifkf2"
   [(set (match_operand:KF 0 "gpc_reg_operand")
-	(float_truncate:KF (match_operand:IF 1 "gpc_reg_operand")))]
-  "TARGET_FLOAT128_TYPE"
-{
-  rs6000_expand_float128_convert (operands[0], operands[1], false);
-  DONE;
-})
-
-(define_expand "trunckftf2"
-  [(set (match_operand:TF 0 "gpc_reg_operand")
-	(float_truncate:TF (match_operand:KF 1 "gpc_reg_operand")))]
+	(float_extend:KF (match_operand:IF 1 "gpc_reg_operand")))]
   "TARGET_FLOAT128_TYPE"
 {
-  rs6000_expand_float128_convert (operands[0], operands[1], false);
-  DONE;
+  gcc_assert (false);
 })

-(define_expand "trunctfif2"
+(define_expand "extendkfif2"
   [(set (match_operand:IF 0 "gpc_reg_operand")
-	(float_truncate:IF (match_operand:TF 1 "gpc_reg_operand")))]
+	(float_extend:IF (match_operand:KF 1 "gpc_reg_operand")))]
   "TARGET_FLOAT128_TYPE"
 {
-  rs6000_expand_float128_convert (operands[0], operands[1], false);
-  DONE;
+  gcc_assert (false);
 })

 (define_insn_and_split "*extend<mode>tf2_internal"
@@ -15161,7 +15125,7 @@  (define_insn "extend<SFDF:mode><IEEE128:mode>2_hw"

 ;; Conversion between KFmode and TFmode if TFmode is ieee 128-bit floating
 ;; point is a simple copy.
-(define_insn_and_split "extendkftf2"
+(define_insn_and_split "*extendkftf2"
   [(set (match_operand:TF 0 "vsx_register_operand" "=wa,?wa")
 	(float_extend:TF (match_operand:KF 1 "vsx_register_operand" "0,wa")))]
   "TARGET_FLOAT128_TYPE && TARGET_IEEEQUAD"
@@ -15177,7 +15141,7 @@  (define_insn_and_split "extendkftf2"
   [(set_attr "type" "*,veclogical")
    (set_attr "length" "0,4")])

-(define_insn_and_split "trunctfkf2"
+(define_insn_and_split "*extendtfkf2"
   [(set (match_operand:KF 0 "vsx_register_operand" "=wa,?wa")
 	(float_extend:KF (match_operand:TF 1 "vsx_register_operand" "0,wa")))]
   "TARGET_FLOAT128_TYPE && TARGET_IEEEQUAD"
diff --git a/gcc/config/rs6000/t-rs6000 b/gcc/config/rs6000/t-rs6000
index b3ce09d523b..155788de40a 100644
--- a/gcc/config/rs6000/t-rs6000
+++ b/gcc/config/rs6000/t-rs6000
@@ -19,7 +19,6 @@ 
 # <http://www.gnu.org/licenses/>.

 TM_H += $(srcdir)/config/rs6000/rs6000-cpus.def
-TM_H += $(srcdir)/config/rs6000/rs6000-modes.h
 PASSES_EXTRA += $(srcdir)/config/rs6000/rs6000-passes.def
 EXTRA_GTYPE_DEPS += rs6000-builtins.h

diff --git a/gcc/doc/tm.texi b/gcc/doc/tm.texi
index c8b8b126b24..7c8b7fcabba 100644
--- a/gcc/doc/tm.texi
+++ b/gcc/doc/tm.texi
@@ -1044,6 +1044,12 @@  are zero or sign extended depending on if it is
 @code{GET_MODE_ALIGNMENT (info->limb_mode)}.
 @end deftypefn

+@deftypefn {Target Hook} machine_mode TARGET_C_MODE_FOR_LONGDOUBLE ()
+Return machine mode for long double type, use it if it is not
+ @code{VOIDmode}, otherwise find an appropriate machine mode with
+ mode precision @code{LONG_DOUBLE_TYPE_SIZE}.
+@end deftypefn
+
 @deftypefn {Target Hook} machine_mode TARGET_PROMOTE_FUNCTION_MODE (const_tree @var{type}, machine_mode @var{mode}, int *@var{punsignedp}, const_tree @var{funtype}, int @var{for_return})
 Like @code{PROMOTE_MODE}, but it is applied to outgoing function arguments or
 function return values.  The target hook should return the new mode
diff --git a/gcc/doc/tm.texi.in b/gcc/doc/tm.texi.in
index 658e1e63371..a26f4a11ac6 100644
--- a/gcc/doc/tm.texi.in
+++ b/gcc/doc/tm.texi.in
@@ -947,6 +947,8 @@  applied.

 @hook TARGET_C_BITINT_TYPE_INFO

+@hook TARGET_C_MODE_FOR_LONGDOUBLE
+
 @hook TARGET_PROMOTE_FUNCTION_MODE

 @defmac PARM_BOUNDARY
diff --git a/gcc/expr.cc b/gcc/expr.cc
index 8d34d024c9c..f5230b9cc44 100644
--- a/gcc/expr.cc
+++ b/gcc/expr.cc
@@ -339,6 +339,26 @@  convert_mode_scalar (rtx to, rtx from, int unsignedp)
   enum rtx_code equiv_code = (unsignedp < 0 ? UNKNOWN
 			      : (unsignedp ? ZERO_EXTEND : SIGN_EXTEND));

+  auto acceptable_same_precision_modes
+    = [] (scalar_mode from_mode, scalar_mode to_mode) {
+	if (DECIMAL_FLOAT_MODE_P (from_mode) != DECIMAL_FLOAT_MODE_P (to_mode))
+	  return true;
+	/* arm_bfloat_half_format <-> ieee_half_format */
+	if ((REAL_MODE_FORMAT (from_mode) == &arm_bfloat_half_format
+	     && REAL_MODE_FORMAT (to_mode) == &ieee_half_format)
+	    || (REAL_MODE_FORMAT (to_mode) == &arm_bfloat_half_format
+		&& REAL_MODE_FORMAT (from_mode) == &ieee_half_format))
+	  return true;
+	/* ibm_extended_format <-> ieee_quad_format */
+	if ((REAL_MODE_FORMAT (from_mode) == &ibm_extended_format
+	     && REAL_MODE_FORMAT (to_mode) == &ieee_quad_format)
+	    || (REAL_MODE_FORMAT (from_mode) == &ieee_quad_format
+		&& REAL_MODE_FORMAT (to_mode) == &ibm_extended_format))
+	  return true;
+
+	return false;
+      };
+
   if (to_real)
     {
       rtx value;
@@ -347,12 +367,7 @@  convert_mode_scalar (rtx to, rtx from, int unsignedp)

       gcc_assert ((GET_MODE_PRECISION (from_mode)
 		   != GET_MODE_PRECISION (to_mode))
-		  || (DECIMAL_FLOAT_MODE_P (from_mode)
-		      != DECIMAL_FLOAT_MODE_P (to_mode))
-		  || (REAL_MODE_FORMAT (from_mode) == &arm_bfloat_half_format
-		      && REAL_MODE_FORMAT (to_mode) == &ieee_half_format)
-		  || (REAL_MODE_FORMAT (to_mode) == &arm_bfloat_half_format
-		      && REAL_MODE_FORMAT (from_mode) == &ieee_half_format));
+		  || acceptable_same_precision_modes (from_mode, to_mode));

       if (GET_MODE_PRECISION (from_mode) == GET_MODE_PRECISION (to_mode))
 	/* Conversion between decimal float and binary float, same size.  */
diff --git a/gcc/target.def b/gcc/target.def
index fdad7bbc93e..2b821612e80 100644
--- a/gcc/target.def
+++ b/gcc/target.def
@@ -6370,6 +6370,13 @@  are zero or sign extended depending on if it is\n\
  bool, (int n, struct bitint_info *info),
  default_bitint_type_info)

+DEFHOOK
+(mode_for_longdouble,
+"Return machine mode for long double type, use it if it is not\n\
+ @code{VOIDmode}, otherwise find an appropriate machine mode with\n\
+ mode precision @code{LONG_DOUBLE_TYPE_SIZE}.",
+ machine_mode, (), default_mode_for_longdouble)
+
 HOOK_VECTOR_END (c)

 /* Functions specific to the C++ frontend.  */
diff --git a/gcc/targhooks.cc b/gcc/targhooks.cc
index 8c84deea97d..b68025acff4 100644
--- a/gcc/targhooks.cc
+++ b/gcc/targhooks.cc
@@ -298,6 +298,15 @@  default_mode_for_suffix (char suffix ATTRIBUTE_UNUSED)
   return VOIDmode;
 }

+/* Return machine mode for long double node type.
+   VOIDmode means caller should find an appropriate one with
+   mode precision LONG_DOUBLE_TYPE_SIZE.  */
+machine_mode
+default_mode_for_longdouble ()
+{
+  return VOIDmode;
+}
+
 /* The generic C++ ABI specifies this is a 64-bit value.  */
 tree
 default_cxx_guard_type (void)
diff --git a/gcc/targhooks.h b/gcc/targhooks.h
index ca289f30eda..7b720c1064d 100644
--- a/gcc/targhooks.h
+++ b/gcc/targhooks.h
@@ -62,6 +62,7 @@  extern tree default_external_stack_protect_fail (void);
 extern tree default_hidden_stack_protect_fail (void);

 extern machine_mode default_mode_for_suffix (char);
+extern machine_mode default_mode_for_longdouble ();

 extern tree default_cxx_guard_type (void);
 extern tree default_cxx_get_cookie_size (tree);
diff --git a/gcc/tree.cc b/gcc/tree.cc
index f730981ec8b..cc7721cab26 100644
--- a/gcc/tree.cc
+++ b/gcc/tree.cc
@@ -9565,6 +9565,7 @@  build_common_tree_nodes (bool signed_char)

   long_double_type_node = make_node (REAL_TYPE);
   TYPE_PRECISION (long_double_type_node) = LONG_DOUBLE_TYPE_SIZE;
+  SET_TYPE_MODE (long_double_type_node, targetm.c.mode_for_longdouble ());
   layout_type (long_double_type_node);

   for (i = 0; i < NUM_FLOATN_NX_TYPES; i++)