From 686ee9719a4dc70619da0a69a4357007406c9fbd Mon Sep 17 00:00:00 2001 From: Richard Sandiford Date: Tue, 17 Nov 2015 18:39:02 +0000 Subject: [PATCH] Add internal math functions 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 | 19 +++++++++++++++ gcc/builtins.c | 58 +++++++++++++++++++++++++++++++++++++++++++++ gcc/builtins.h | 3 +++ gcc/internal-fn.c | 57 ++++++++++++++++++++++++++++++++++++++++++++ gcc/internal-fn.def | 50 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 187 insertions(+) diff --git a/gcc/ChangeLog b/gcc/ChangeLog index bdb295c6f24..504138eada8 100644 --- a/gcc/ChangeLog +++ b/gcc/ChangeLog @@ -1,3 +1,22 @@ +2015-11-17 Richard Sandiford + + * 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 * coretypes.h (tree_pair): New type. diff --git a/gcc/builtins.c b/gcc/builtins.c index 85b251ae092..0eef1125027 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 b0396322fa0..7f92d076d2e 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 e77006d4dd3..b8531628f9f 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, 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 diff --git a/gcc/internal-fn.def b/gcc/internal-fn.def index a5f6df26a91..65e158e23b8 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_{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 -- 2.30.2