Add internal math functions

Richard Sandiford richard.sandiford@arm.com
Sat Nov 7 12:31:00 GMT 2015


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.
OK to install?

Thanks,
Richard


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.

diff --git a/gcc/builtins.c b/gcc/builtins.c
index ad661c1..0a9b185 100644
--- a/gcc/builtins.c
+++ b/gcc/builtins.c
@@ -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.  */
diff --git a/gcc/builtins.h b/gcc/builtins.h
index b039632..7f92d07 100644
--- a/gcc/builtins.h
+++ b/gcc/builtins.h
@@ -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
diff --git a/gcc/internal-fn.c b/gcc/internal-fn.c
index 72536da..9f9f9cf 100644
--- a/gcc/internal-fn.c
+++ b/gcc/internal-fn.c
@@ -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, 3 }
 #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,
@@ -2066,6 +2069,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
@@ -2117,6 +2172,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
diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def
index a5f6df2..65e158e 100644
--- a/gcc/internal-fn.def
+++ b/gcc/internal-fn.def
@@ -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



More information about the Gcc-patches mailing list