rs6000.c (rs6000_atomic_assign_expand_fenv): New function.
authorAdhemerval Zanella <azanella@linux.vnet.ibm.com>
Sun, 19 Oct 2014 13:49:26 +0000 (13:49 +0000)
committerDavid Edelsohn <dje@gcc.gnu.org>
Sun, 19 Oct 2014 13:49:26 +0000 (09:49 -0400)
2014-10-19  Adhemerval Zanella  <azanella@linux.vnet.ibm.com>
            David Edelsohn  <dje.gcc@gmail.com>

        * config/rs6000/rs6000.c (rs6000_atomic_assign_expand_fenv): New
        function.
        (TARGET_ATOMIC_ASSIGN_EXPAND_FENV): New define.
* gcc.dg/atomic/c11-atomic-exec-5.c
(test_main_long_double_add_overflow): Define and run only for
LDBL_MANT_DIG != 106.
(test_main_complex_long_double_add_overflow): Likewise.
(test_main_long_double_sub_overflow): Likewise.
(test_main_complex_long_double_sub_overflow): Likewise.

Co-Authored-By: David Edelsohn <dje.gcc@gmail.com>
From-SVN: r216437

gcc/ChangeLog
gcc/config/rs6000/rs6000.c
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.dg/atomic/c11-atomic-exec-5.c

index 896e77f7ecfb201b15d8d692b9953d9840d96ca7..9a7f7d2800c9a3b791d7d433e0dea2ea35f9a1d9 100644 (file)
@@ -1,3 +1,10 @@
+2014-10-19  Adhemerval Zanella  <azanella@linux.vnet.ibm.com>
+           David Edelsohn  <dje.gcc@gmail.com>
+
+       * config/rs6000/rs6000.c (rs6000_atomic_assign_expand_fenv): New
+       function.
+       (TARGET_ATOMIC_ASSIGN_EXPAND_FENV): New define.
+
 2014-10-18  Manuel López-Ibáñez  <manu@gcc.gnu.org>
 
        * doc/invoke.texi (Options to Request or Suppress Warnings):
index c4510323deff1edc1d3459f227ed50dddca76ee8..2f14c2b6c3e3335fb065fa6043f7d27a7aae72de 100644 (file)
@@ -1647,6 +1647,9 @@ static const struct attribute_spec rs6000_attribute_table[] =
 
 #undef TARGET_CAN_USE_DOLOOP_P
 #define TARGET_CAN_USE_DOLOOP_P can_use_doloop_if_innermost
+
+#undef TARGET_ATOMIC_ASSIGN_EXPAND_FENV
+#define TARGET_ATOMIC_ASSIGN_EXPAND_FENV rs6000_atomic_assign_expand_fenv
 \f
 
 /* Processor table.  */
@@ -34575,6 +34578,117 @@ make_pass_analyze_swaps (gcc::context *ctxt)
 {
   return new pass_analyze_swaps (ctxt);
 }
+
+/* Implement TARGET_ATOMIC_ASSIGN_EXPAND_FENV hook.  */
+
+static void
+rs6000_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update)
+{
+  if (!TARGET_HARD_FLOAT || !TARGET_FPRS)
+    return;
+
+  tree mffs = rs6000_builtin_decls[RS6000_BUILTIN_MFFS];
+  tree mtfsf = rs6000_builtin_decls[RS6000_BUILTIN_MTFSF];
+  tree call_mffs = build_call_expr (mffs, 0);
+
+  /* Generates the equivalent of feholdexcept (&fenv_var)
+
+     *fenv_var = __builtin_mffs ();
+     double fenv_hold;
+     *(uint64_t*)&fenv_hold = *(uint64_t*)fenv_var & 0xffffffff00000007LL;
+     __builtin_mtfsf (0xff, fenv_hold);  */
+
+  /* Mask to clear everything except for the rounding modes and non-IEEE
+     arithmetic flag.  */
+  const unsigned HOST_WIDE_INT hold_exception_mask =
+    HOST_WIDE_INT_C (0xffffffff00000007);
+
+  tree fenv_var = create_tmp_var (double_type_node, NULL);
+
+  tree hold_mffs = build2 (MODIFY_EXPR, void_type_node, fenv_var, call_mffs);
+
+  tree fenv_llu = build1 (VIEW_CONVERT_EXPR, uint64_type_node, fenv_var);
+  tree fenv_llu_and = build2 (BIT_AND_EXPR, uint64_type_node, fenv_llu,
+                             build_int_cst (uint64_type_node,
+                                            hold_exception_mask));
+
+  tree fenv_hold_mtfsf = build1 (VIEW_CONVERT_EXPR, double_type_node,
+                                fenv_llu_and);
+
+  tree hold_mtfsf = build_call_expr (mtfsf, 2,
+                                    build_int_cst (unsigned_type_node, 0xff),
+                                    fenv_hold_mtfsf);
+
+  *hold = build2 (COMPOUND_EXPR, void_type_node, hold_mffs, hold_mtfsf);
+
+  /* Generates the equivalent of feclearexcept (FE_ALL_EXCEPT):
+
+     double fenv_clear = __builtin_mffs ();
+     *(uint64_t)&fenv_clear &= 0xffffffff00000000LL;
+     __builtin_mtfsf (0xff, fenv_clear);  */
+
+  /* Mask to clear everything except for the rounding modes and non-IEEE
+     arithmetic flag.  */
+  const unsigned HOST_WIDE_INT clear_exception_mask =
+    HOST_WIDE_INT_C (0xffffffff00000000);
+
+  tree fenv_clear = create_tmp_var (double_type_node, NULL);
+
+  tree clear_mffs = build2 (MODIFY_EXPR, void_type_node, fenv_clear, call_mffs);
+
+  tree fenv_clean_llu = build1 (VIEW_CONVERT_EXPR, uint64_type_node, fenv_clear);
+  tree fenv_clear_llu_and = build2 (BIT_AND_EXPR, uint64_type_node,
+                                   fenv_clean_llu,
+                                   build_int_cst (uint64_type_node,
+                                                  clear_exception_mask));
+
+  tree fenv_clear_mtfsf = build1 (VIEW_CONVERT_EXPR, double_type_node,
+                                 fenv_clear_llu_and);
+
+  tree clear_mtfsf = build_call_expr (mtfsf, 2,
+                                     build_int_cst (unsigned_type_node, 0xff),
+                                     fenv_clear_mtfsf);
+
+  *clear = build2 (COMPOUND_EXPR, void_type_node, clear_mffs, clear_mtfsf);
+
+  /* Generates the equivalent of feupdateenv (&fenv_var)
+
+     double old_fenv = __builtin_mffs ();
+     double fenv_update;
+     *(uint64_t*)&fenv_update = (*(uint64_t*)&old & 0xffffffff1fffff00LL) |
+                                (*(uint64_t*)fenv_var 0x1ff80fff);
+     __builtin_mtfsf (0xff, fenv_update);  */
+
+  const unsigned HOST_WIDE_INT update_exception_mask =
+    HOST_WIDE_INT_C (0xffffffff1fffff00);
+  const unsigned HOST_WIDE_INT new_exception_mask =
+    HOST_WIDE_INT_C (0x1ff80fff);
+
+  tree old_fenv = create_tmp_var (double_type_node, NULL);
+  tree update_mffs = build2 (MODIFY_EXPR, void_type_node, old_fenv, call_mffs);
+
+  tree old_llu = build1 (VIEW_CONVERT_EXPR, uint64_type_node, old_fenv);
+  tree old_llu_and = build2 (BIT_AND_EXPR, uint64_type_node, old_llu,
+                            build_int_cst (uint64_type_node,
+                                           update_exception_mask));
+
+  tree new_llu_and = build2 (BIT_AND_EXPR, uint64_type_node, fenv_llu,
+                            build_int_cst (uint64_type_node,
+                                           new_exception_mask));
+
+  tree new_llu_mask = build2 (BIT_IOR_EXPR, uint64_type_node,
+                             old_llu_and, new_llu_and);
+
+  tree fenv_update_mtfsf = build1 (VIEW_CONVERT_EXPR, double_type_node,
+                                  new_llu_mask);
+
+  tree update_mtfsf = build_call_expr (mtfsf, 2,
+                                      build_int_cst (unsigned_type_node, 0xff),
+                                      fenv_update_mtfsf);
+
+  *update = build2 (COMPOUND_EXPR, void_type_node, update_mffs, update_mtfsf);
+}
+
 \f
 struct gcc_target targetm = TARGET_INITIALIZER;
 
index 87ec5cb32ae1bb888aea63c20c12134264baa07e..88b321b07c9164c56eed26cd425895586b49a445 100644 (file)
@@ -1,3 +1,12 @@
+2014-10-19  Adhemerval Zanella  <azanella@linux.vnet.ibm.com>
+
+       * gcc.dg/atomic/c11-atomic-exec-5.c
+       (test_main_long_double_add_overflow): Define and run only for
+       LDBL_MANT_DIG != 106.
+       (test_main_complex_long_double_add_overflow): Likewise.
+       (test_main_long_double_sub_overflow): Likewise.
+       (test_main_complex_long_double_sub_overflow): Likewise.
+
 2014-10-18  Paul Thomas  <pault@gcc.gnu.org>
 
        PR fortran/63553
index 2b2b1be4af744ae9c42428174655fd0e5aea158b..7166042ba7f47af854f70d787bca41d500829f3e 100644 (file)
@@ -507,23 +507,23 @@ main (void)
   ret |= test_main_int_div_double_inexact ();
   ret |= test_main_complex_double_div_overflow ();
   ret |= test_main_long_double_add_invalid ();
-  ret |= test_main_long_double_add_overflow ();
 #if LDBL_MANT_DIG != 106
+  ret |= test_main_long_double_add_overflow ();
   ret |= test_main_long_double_add_inexact ();
   ret |= test_main_long_double_add_inexact_int ();
   ret |= test_main_long_double_preinc_inexact ();
   ret |= test_main_long_double_postinc_inexact ();
-#endif
   ret |= test_main_complex_long_double_add_overflow ();
+#endif
   ret |= test_main_long_double_sub_invalid ();
-  ret |= test_main_long_double_sub_overflow ();
 #if LDBL_MANT_DIG != 106
+  ret |= test_main_long_double_sub_overflow ();
   ret |= test_main_long_double_sub_inexact ();
   ret |= test_main_long_double_sub_inexact_int ();
   ret |= test_main_long_double_predec_inexact ();
   ret |= test_main_long_double_postdec_inexact ();
-#endif
   ret |= test_main_complex_long_double_sub_overflow ();
+#endif
   ret |= test_main_long_double_mul_invalid ();
   ret |= test_main_long_double_mul_overflow ();
   ret |= test_main_long_double_mul_overflow_float ();