+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.
#include "cilk.h"
#include "tree-chkp.h"
#include "rtl-chkp.h"
+#include "internal-fn.h"
struct target_builtins default_target_builtins;
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. */
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
#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[] = {
#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,
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
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
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.
- 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)
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)
/* 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