Add internal math functions
authorRichard Sandiford <richard.sandiford@arm.com>
Tue, 17 Nov 2015 18:39:02 +0000 (18:39 +0000)
committerRichard Sandiford <rsandifo@gcc.gnu.org>
Tue, 17 Nov 2015 18:39:02 +0000 (18:39 +0000)
This patch adds internal functions for simple FLT_FN built-in functions,
in cases where an associated optab already exists.  Unlike some of the
built-in functions, these internal functions never set errno.

LDEXP is an odd-one out in that its second operand is an integer.
All the others operate on uniform types.

The patch also adds a function to query the internal function associated
with a built-in function (if any), and another to test whether a given
gcall could be replaced by a call to an internal function on the current
target (as long as the caller deals with errno appropriately).

Tested on x86_64-linux-gnu, aarch64-linux-gnu and arm-linux-gnueabi.

gcc/
* builtins.h (associated_internal_fn): Declare.
(replacement_internal_fn): Likewise.
* builtins.c: Include internal-fn.h
(associated_internal_fn, replacement_internal_fn): New functions.
* internal-fn.def (DEF_INTERNAL_FLT_FN): New macro.
(ACOS, ASIN, ATAN, COS, EXP, EXP10, EXP2, EXPM1, LOG, LOG10, LOG1P)
(LOG2, LOGB, SIGNIFICAND, SIN, SQRT, TAN, CEIL, FLOOR, NEARBYINT)
(RINT, ROUND, TRUNC, ATAN2, COPYSIGN, FMOD, POW, REMAINDER, SCALB)
(LDEXP): New functions.
* internal-fn.c: Include recog.h.
(unary_direct, binary_direct): New macros.
(expand_direct_optab_fn): New function.
(expand_unary_optab_fn): New macro.
(expand_binary_optab_fn): Likewise.
(direct_unary_optab_supported_p): Likewise.
(direct_binary_optab_supported_p): Likewise.

From-SVN: r230474

gcc/ChangeLog
gcc/builtins.c
gcc/builtins.h
gcc/internal-fn.c
gcc/internal-fn.def

index bdb295c6f2463b01a4c1002eac451ca1473b9e68..504138eada8153f6d1d9e71dfaef7a9f4e1daee3 100644 (file)
@@ -1,3 +1,22 @@
+2015-11-17  Richard Sandiford  <richard.sandiford@arm.com>
+
+       * builtins.h (associated_internal_fn): Declare.
+       (replacement_internal_fn): Likewise.
+       * builtins.c: Include internal-fn.h
+       (associated_internal_fn, replacement_internal_fn): New functions.
+       * internal-fn.def (DEF_INTERNAL_FLT_FN): New macro.
+       (ACOS, ASIN, ATAN, COS, EXP, EXP10, EXP2, EXPM1, LOG, LOG10, LOG1P)
+       (LOG2, LOGB, SIGNIFICAND, SIN, SQRT, TAN, CEIL, FLOOR, NEARBYINT)
+       (RINT, ROUND, TRUNC, ATAN2, COPYSIGN, FMOD, POW, REMAINDER, SCALB)
+       (LDEXP): New functions.
+       * internal-fn.c: Include recog.h.
+       (unary_direct, binary_direct): New macros.
+       (expand_direct_optab_fn): New function.
+       (expand_unary_optab_fn): New macro.
+       (expand_binary_optab_fn): Likewise.
+       (direct_unary_optab_supported_p): Likewise.
+       (direct_binary_optab_supported_p): Likewise.
+
 2015-11-17  Richard Sandiford  <richard.sandiford@arm.com>
 
        * coretypes.h (tree_pair): New type.
index 85b251ae0922f6caadb33ec08696a477b563a990..0eef1125027d49f4a0c66eef67d92fcefb442435 100644 (file)
@@ -62,6 +62,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "cilk.h"
 #include "tree-chkp.h"
 #include "rtl-chkp.h"
+#include "internal-fn.h"
 
 
 struct target_builtins default_target_builtins;
@@ -1901,6 +1902,63 @@ mathfn_built_in (tree type, enum built_in_function fn)
   return mathfn_built_in_1 (type, fn, /*implicit=*/ 1);
 }
 
+/* If BUILT_IN_NORMAL function FNDECL has an associated internal function,
+   return its code, otherwise return IFN_LAST.  Note that this function
+   only tests whether the function is defined in internals.def, not whether
+   it is actually available on the target.  */
+
+internal_fn
+associated_internal_fn (tree fndecl)
+{
+  gcc_checking_assert (DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL);
+  tree return_type = TREE_TYPE (TREE_TYPE (fndecl));
+  switch (DECL_FUNCTION_CODE (fndecl))
+    {
+#define DEF_INTERNAL_FLT_FN(NAME, FLAGS, OPTAB, TYPE) \
+    CASE_FLT_FN (BUILT_IN_##NAME): return IFN_##NAME;
+#include "internal-fn.def"
+
+    CASE_FLT_FN (BUILT_IN_POW10):
+      return IFN_EXP10;
+
+    CASE_FLT_FN (BUILT_IN_DREM):
+      return IFN_REMAINDER;
+
+    CASE_FLT_FN (BUILT_IN_SCALBN):
+    CASE_FLT_FN (BUILT_IN_SCALBLN):
+      if (REAL_MODE_FORMAT (TYPE_MODE (return_type))->b == 2)
+       return IFN_LDEXP;
+      return IFN_LAST;
+
+    default:
+      return IFN_LAST;
+    }
+}
+
+/* If CALL is a call to a BUILT_IN_NORMAL function that could be replaced
+   on the current target by a call to an internal function, return the
+   code of that internal function, otherwise return IFN_LAST.  The caller
+   is responsible for ensuring that any side-effects of the built-in
+   call are dealt with correctly.  E.g. if CALL sets errno, the caller
+   must decide that the errno result isn't needed or make it available
+   in some other way.  */
+
+internal_fn
+replacement_internal_fn (gcall *call)
+{
+  if (gimple_call_builtin_p (call, BUILT_IN_NORMAL))
+    {
+      internal_fn ifn = associated_internal_fn (gimple_call_fndecl (call));
+      if (ifn != IFN_LAST)
+       {
+         tree_pair types = direct_internal_fn_types (ifn, call);
+         if (direct_internal_fn_supported_p (ifn, types))
+           return ifn;
+       }
+    }
+  return IFN_LAST;
+}
+
 /* If errno must be maintained, expand the RTL to check if the result,
    TARGET, of a built-in function call, EXP, is NaN, and if so set
    errno to EDOM.  */
index b0396322fa0b1c10b9e8b5d7c351efa3a6c3ba67..7f92d076d2e08fface691c15378821315ba5583c 100644 (file)
@@ -94,4 +94,7 @@ extern char target_percent_s[3];
 extern char target_percent_c[3];
 extern char target_percent_s_newline[4];
 
+extern internal_fn associated_internal_fn (tree);
+extern internal_fn replacement_internal_fn (gcall *);
+
 #endif
index e77006d4dd317eede139fac7349b952b80ed43b2..b8531628f9f5922143caa633c7b6a5728f4953fa 100644 (file)
@@ -38,6 +38,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "dojump.h"
 #include "expr.h"
 #include "ubsan.h"
+#include "recog.h"
 
 /* The names of each internal function, indexed by function number.  */
 const char *const internal_fn_name_array[] = {
@@ -73,6 +74,8 @@ init_internal_fns ()
 #define load_lanes_direct { -1, -1 }
 #define mask_store_direct { 3, 2 }
 #define store_lanes_direct { 0, 0 }
+#define unary_direct { 0, 0 }
+#define binary_direct { 0, 0 }
 
 const direct_internal_fn_info direct_internal_fn_array[IFN_LAST + 1] = {
 #define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) not_direct,
@@ -2070,6 +2073,58 @@ expand_GOACC_REDUCTION (gcall *stmt ATTRIBUTE_UNUSED)
   gcc_unreachable ();
 }
 
+/* Expand call STMT using OPTAB, which has a single output operand and
+   NARGS input operands.  */
+
+static void
+expand_direct_optab_fn (gcall *stmt, direct_optab optab, unsigned int nargs)
+{
+  expand_operand *ops = XALLOCAVEC (expand_operand, nargs + 1);
+
+  internal_fn fn = gimple_call_internal_fn (stmt);
+  tree_pair types = direct_internal_fn_types (fn, stmt);
+  insn_code icode = direct_optab_handler (optab, TYPE_MODE (types.first));
+
+  tree lhs = gimple_call_lhs (stmt);
+  tree lhs_type = TREE_TYPE (lhs);
+  rtx lhs_rtx = expand_expr (lhs, NULL_RTX, VOIDmode, EXPAND_WRITE);
+  create_output_operand (&ops[0], lhs_rtx, insn_data[icode].operand[0].mode);
+
+  for (unsigned int i = 0; i < nargs; ++i)
+    {
+      tree rhs = gimple_call_arg (stmt, i);
+      tree rhs_type = TREE_TYPE (rhs);
+      rtx rhs_rtx = expand_normal (rhs);
+      if (INTEGRAL_TYPE_P (rhs_type))
+       create_convert_operand_from (&ops[i + 1], rhs_rtx,
+                                    TYPE_MODE (rhs_type),
+                                    TYPE_UNSIGNED (rhs_type));
+      else
+       create_input_operand (&ops[i + 1], rhs_rtx, TYPE_MODE (rhs_type));
+    }
+
+  expand_insn (icode, nargs + 1, ops);
+  if (!rtx_equal_p (lhs_rtx, ops[0].value))
+    {
+      if (INTEGRAL_TYPE_P (lhs_type))
+       /* Convert the operand to the required type, which is useful
+          for things that return an int regardless of the size of
+          the input.  If the value produced by the instruction is
+          smaller than required, assume that it is signed.  */
+       convert_move (lhs_rtx, ops[0].value, 0);
+      else
+       emit_move_insn (lhs_rtx, ops[0].value);
+    }
+}
+
+/* Expanders for optabs that can use expand_direct_optab_fn.  */
+
+#define expand_unary_optab_fn(STMT, OPTAB) \
+  expand_direct_optab_fn (STMT, OPTAB, 1)
+
+#define expand_binary_optab_fn(STMT, OPTAB) \
+  expand_direct_optab_fn (STMT, OPTAB, 2)
+
 /* RETURN_TYPE and ARGS are a return type and argument list that are
    in principle compatible with FN (which satisfies direct_internal_fn_p).
    Return the types that should be used to determine whether the
@@ -2121,6 +2176,8 @@ multi_vector_optab_supported_p (convert_optab optab, tree_pair types)
   return get_multi_vector_move (types.first, optab) != CODE_FOR_nothing;
 }
 
+#define direct_unary_optab_supported_p direct_optab_supported_p
+#define direct_binary_optab_supported_p direct_optab_supported_p
 #define direct_mask_load_optab_supported_p direct_optab_supported_p
 #define direct_load_lanes_optab_supported_p multi_vector_optab_supported_p
 #define direct_mask_store_optab_supported_p direct_optab_supported_p
index a5f6df26a91f57f71b922ff6e19edb567437c22d..65e158e23b84e76da95e07f82337aeaf16e444b8 100644 (file)
@@ -30,6 +30,7 @@ along with GCC; see the file COPYING3.  If not see
 
      DEF_INTERNAL_FN (NAME, FLAGS, FNSPEC)
      DEF_INTERNAL_OPTAB_FN (NAME, FLAGS, OPTAB, TYPE)
+     DEF_INTERNAL_FLT_FN (NAME, FLAGS, OPTAB, TYPE)
 
    where NAME is the name of the function, FLAGS is a set of
    ECF_* flags and FNSPEC is a string describing functions fnspec.
@@ -47,6 +48,11 @@ along with GCC; see the file COPYING3.  If not see
    - mask_store: currently just maskstore
    - store_lanes: currently just vec_store_lanes
 
+   DEF_INTERNAL_FLT_FN is like DEF_INTERNAL_OPTAB_FN, but in addition,
+   the function implements the computational part of a built-in math
+   function BUILT_IN_<NAME>{F,,L}.  Unlike some built-in functions,
+   these internal functions never set errno.
+
    Each entry must have a corresponding expander of the form:
 
      void expand_NAME (gimple_call stmt)
@@ -64,12 +70,55 @@ along with GCC; see the file COPYING3.  If not see
   DEF_INTERNAL_FN (NAME, FLAGS | ECF_LEAF, NULL)
 #endif
 
+#ifndef DEF_INTERNAL_FLT_FN
+#define DEF_INTERNAL_FLT_FN(NAME, FLAGS, OPTAB, TYPE) \
+  DEF_INTERNAL_OPTAB_FN (NAME, FLAGS, OPTAB, TYPE)
+#endif
+
 DEF_INTERNAL_OPTAB_FN (MASK_LOAD, ECF_PURE, maskload, mask_load)
 DEF_INTERNAL_OPTAB_FN (LOAD_LANES, ECF_CONST, vec_load_lanes, load_lanes)
 
 DEF_INTERNAL_OPTAB_FN (MASK_STORE, 0, maskstore, mask_store)
 DEF_INTERNAL_OPTAB_FN (STORE_LANES, ECF_CONST, vec_store_lanes, store_lanes)
 
+/* Unary math functions.  */
+DEF_INTERNAL_FLT_FN (ACOS, ECF_CONST, acos, unary)
+DEF_INTERNAL_FLT_FN (ASIN, ECF_CONST, asin, unary)
+DEF_INTERNAL_FLT_FN (ATAN, ECF_CONST, atan, unary)
+DEF_INTERNAL_FLT_FN (COS, ECF_CONST, cos, unary)
+DEF_INTERNAL_FLT_FN (EXP, ECF_CONST, exp, unary)
+DEF_INTERNAL_FLT_FN (EXP10, ECF_CONST, exp10, unary)
+DEF_INTERNAL_FLT_FN (EXP2, ECF_CONST, exp2, unary)
+DEF_INTERNAL_FLT_FN (EXPM1, ECF_CONST, expm1, unary)
+DEF_INTERNAL_FLT_FN (LOG, ECF_CONST, log, unary)
+DEF_INTERNAL_FLT_FN (LOG10, ECF_CONST, log10, unary)
+DEF_INTERNAL_FLT_FN (LOG1P, ECF_CONST, log1p, unary)
+DEF_INTERNAL_FLT_FN (LOG2, ECF_CONST, log2, unary)
+DEF_INTERNAL_FLT_FN (LOGB, ECF_CONST, logb, unary)
+DEF_INTERNAL_FLT_FN (SIGNIFICAND, ECF_CONST, significand, unary)
+DEF_INTERNAL_FLT_FN (SIN, ECF_CONST, sin, unary)
+DEF_INTERNAL_FLT_FN (SQRT, ECF_CONST, sqrt, unary)
+DEF_INTERNAL_FLT_FN (TAN, ECF_CONST, tan, unary)
+
+/* FP rounding.  */
+DEF_INTERNAL_FLT_FN (CEIL, ECF_CONST, ceil, unary)
+DEF_INTERNAL_FLT_FN (FLOOR, ECF_CONST, floor, unary)
+DEF_INTERNAL_FLT_FN (NEARBYINT, ECF_CONST, nearbyint, unary)
+DEF_INTERNAL_FLT_FN (RINT, ECF_CONST, rint, unary)
+DEF_INTERNAL_FLT_FN (ROUND, ECF_CONST, round, unary)
+DEF_INTERNAL_FLT_FN (TRUNC, ECF_CONST, btrunc, unary)
+
+/* Binary math functions.  */
+DEF_INTERNAL_FLT_FN (ATAN2, ECF_CONST, atan2, binary)
+DEF_INTERNAL_FLT_FN (COPYSIGN, ECF_CONST, copysign, binary)
+DEF_INTERNAL_FLT_FN (FMOD, ECF_CONST, fmod, binary)
+DEF_INTERNAL_FLT_FN (POW, ECF_CONST, pow, binary)
+DEF_INTERNAL_FLT_FN (REMAINDER, ECF_CONST, remainder, binary)
+DEF_INTERNAL_FLT_FN (SCALB, ECF_CONST, scalb, binary)
+
+/* FP scales.  */
+DEF_INTERNAL_FLT_FN (LDEXP, ECF_CONST, ldexp, binary)
+
 DEF_INTERNAL_FN (GOMP_SIMD_LANE, ECF_NOVOPS | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (GOMP_SIMD_VF, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
 DEF_INTERNAL_FN (GOMP_SIMD_LAST_LANE, ECF_CONST | ECF_LEAF | ECF_NOTHROW, NULL)
@@ -114,5 +163,6 @@ DEF_INTERNAL_FN (GOACC_LOOP, ECF_PURE | ECF_NOTHROW, NULL)
 /* OpenACC reduction abstraction.  See internal-fn.h  for usage.  */
 DEF_INTERNAL_FN (GOACC_REDUCTION, ECF_NOTHROW | ECF_LEAF, NULL)
 
+#undef DEF_INTERNAL_FLT_FN
 #undef DEF_INTERNAL_OPTAB_FN
 #undef DEF_INTERNAL_FN