We do not have a direct conversion instruction from 128 bit DFP to 32
bit DFP so this needs to be done in two steps.  The first needs to be
done with the "prepare for shorter precision rounding mode" in order
to produce a correct result.

2016-03-08  Andreas Krebbel  <kreb...@linux.vnet.ibm.com>

        * config/s390/s390.md ("trunctddd2"): Turn former define_insn into
        define_expand.
        ("*trunctddd2"): New pattern definition.
        ("trunctdsd2"): Set prep_for_short_prec rounding mode for the
        TD->DD truncation.

gcc/testsuite/ChangeLog:

2016-03-08  Andreas Krebbel  <kreb...@linux.vnet.ibm.com>

        * gcc.target/s390/dfp-1.c: New test.
---
 gcc/config/s390/s390.md               | 17 ++++++++++++++---
 gcc/testsuite/gcc.target/s390/dfp-1.c | 23 +++++++++++++++++++++++
 2 files changed, 37 insertions(+), 3 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/s390/dfp-1.c

diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md
index 185a3f8..5a9f1c8 100644
--- a/gcc/config/s390/s390.md
+++ b/gcc/config/s390/s390.md
@@ -4847,12 +4847,22 @@
 ; trunctddd2 and truncddsd2 instruction pattern(s).
 ;
 
-(define_insn "trunctddd2"
+
+(define_expand "trunctddd2"
+  [(parallel
+    [(set (match_operand:DD 0 "register_operand" "")
+         (float_truncate:DD (match_operand:TD 1 "register_operand" "")))
+     (unspec:DI [(const_int DFP_RND_CURRENT)] UNSPEC_ROUND)
+     (clobber (scratch:TD))])]
+  "TARGET_HARD_DFP")
+
+(define_insn "*trunctddd2"
   [(set (match_operand:DD 0 "register_operand" "=f")
        (float_truncate:DD (match_operand:TD 1 "register_operand" "f")))
-   (clobber (match_scratch:TD 2 "=f"))]
+   (unspec:DI [(match_operand:DI 2 "const_mask_operand" "I")] UNSPEC_ROUND)
+   (clobber (match_scratch:TD 3 "=f"))]
   "TARGET_HARD_DFP"
-  "ldxtr\t%2,0,%1,0\;ldr\t%0,%2"
+  "ldxtr\t%3,%2,%1,0\;ldr\t%0,%3"
   [(set_attr "length"  "6")
    (set_attr "type"    "ftruncdd")])
 
@@ -4868,6 +4878,7 @@
   [(parallel
     [(set (match_dup 3)
          (float_truncate:DD (match_operand:TD 1 "register_operand" "")))
+     (unspec:DI [(const_int DFP_RND_PREP_FOR_SHORT_PREC)] UNSPEC_ROUND)
      (clobber (match_scratch:TD 2 ""))])
    (set (match_operand:SD 0 "register_operand" "")
        (float_truncate:SD (match_dup 3)))]
diff --git a/gcc/testsuite/gcc.target/s390/dfp-1.c 
b/gcc/testsuite/gcc.target/s390/dfp-1.c
new file mode 100644
index 0000000..109d9fb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/dfp-1.c
@@ -0,0 +1,23 @@
+/* We do not have a direct conversion instruction from 128 bit DFP to
+   32 bit DFP so this needs to be done in two steps.  The first needs
+   to be done with the "prepare for shorter precision rounding mode"
+   in order to produce a correct result.  Otherwise the 8th digit of
+   the number will change from 4 to 5 in the first rounding step which
+   then will turn the last digit of the 32 bit DFP number (the 3) into
+   a 4.  Although with direct rounding it would stay a 3.  */
+
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z10 -mzarch" } */
+
+_Decimal32 __attribute__((noinline))
+foo (_Decimal128 a)
+{
+  return (_Decimal32)a;
+}
+
+int
+main ()
+{
+    if (foo (1.23456349999999999DL) != 1.234563DF)
+    __builtin_abort ();
+}
-- 
1.9.1

Reply via email to