Commit c9e926ce by Richard Sandiford Committed by Richard Sandiford

Add genmatch support for internal functions

This patch makes genmatch match calls based on combined_fn rather
than built_in_function and extends the matching to internal functions.
It also uses fold_const_call to fold the calls to a constant, rather
than going through fold_builtin_n.

In order to slightly simplify the code and remove potential
ambiguity, the patch enforces lower case for tree codes
(foo->FOO_EXPR), caps for functions (no built_in_hypot->BUILT_IN_HYPOT)
and requires an exact match for user-defined identifiers.  The first two
were already met in practice but there were a couple of cases where
operator lists were defined in one case and used in another.

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

gcc/
	* match.pd: Use HYPOT and COS rather than hypot and cos.
	Use CASE_CFN_* macros.  Guard log/exp folds with
	SCALAR_FLOAT_TYPE_P.
	* genmatch.c (internal_fn): New enum.
	(fn_id::fn): Change to an unsigned int.
	(fn_id::fn_id): Accept internal_fn too.
	(add_builtin): Rename to...
	(add_function): ...this and turn into a template.
	(get_operator): Only try one variation if the original name fails.
	Only add _EXPR if the original name was all lower case.
	Try converting internal and built-in function names to their
	CFN equivalents.
	(expr::gen_transform): Use maybe_build_call_expr_loc for generic.
	(dt_simplify::gen_1): Likewise.
	(dt_node::gen_kids_1): Use gimple_call_combined_fn for gimple
	and get_call_combined_fn for generic.
	(dt_simplify::gen): Use combined_fn as the type of fn_ids.
	(decision_tree::gen): Likewise.
	(main): Use lower case in the strings for {VIEW_,}CONVERT[012].
	Use add_function rather than add_builtin.  Register internal
	functions too.
	* generic-match-head.c: Include case-cfn-macros.h.
	* gimple-fold.c (replace_stmt_with_simplification): Use
	gimple_call_combined_fn to test whether we can keep an
	existing call.
	* gimple-match.h (code_helper): Replace built_in_function
	with combined_fn.
	* gimple-match-head.c: Include fold-const-call.h, internal-fn.h
	and case-fn-macros.h.
	(gimple_resimplify1): Use fold_const_call.
	(gimple_resimplify2, gimple_resimplify3): Likewise.
	(build_call_internal, build_call): New functions.
	(maybe_push_res_to_seq): Use them.
	(gimple_simplify): Use fold_const_call.  Set *rcode to a combined_fn
	rather than a built-in function.
	* tree.h (build_call_expr_internal_loc): Declare.
	(maybe_build_call_expr_loc): Likewise.
	* tree.c (build_call_expr_internal_loc_array): New function.
	(maybe_build_call_expr_loc): Likewise.

From-SVN: r230484
parent b03ff92e
2015-11-17 Richard Sandiford <richard.sandiford@arm.com> 2015-11-17 Richard Sandiford <richard.sandiford@arm.com>
* match.pd: Use HYPOT and COS rather than hypot and cos.
Use CASE_CFN_* macros. Guard log/exp folds with
SCALAR_FLOAT_TYPE_P.
* genmatch.c (internal_fn): New enum.
(fn_id::fn): Change to an unsigned int.
(fn_id::fn_id): Accept internal_fn too.
(add_builtin): Rename to...
(add_function): ...this and turn into a template.
(get_operator): Only try one variation if the original name fails.
Only add _EXPR if the original name was all lower case.
Try converting internal and built-in function names to their
CFN equivalents.
(expr::gen_transform): Use maybe_build_call_expr_loc for generic.
(dt_simplify::gen_1): Likewise.
(dt_node::gen_kids_1): Use gimple_call_combined_fn for gimple
and get_call_combined_fn for generic.
(dt_simplify::gen): Use combined_fn as the type of fn_ids.
(decision_tree::gen): Likewise.
(main): Use lower case in the strings for {VIEW_,}CONVERT[012].
Use add_function rather than add_builtin. Register internal
functions too.
* generic-match-head.c: Include case-cfn-macros.h.
* gimple-fold.c (replace_stmt_with_simplification): Use
gimple_call_combined_fn to test whether we can keep an
existing call.
* gimple-match.h (code_helper): Replace built_in_function
with combined_fn.
* gimple-match-head.c: Include fold-const-call.h, internal-fn.h
and case-fn-macros.h.
(gimple_resimplify1): Use fold_const_call.
(gimple_resimplify2, gimple_resimplify3): Likewise.
(build_call_internal, build_call): New functions.
(maybe_push_res_to_seq): Use them.
(gimple_simplify): Use fold_const_call. Set *rcode to a combined_fn
rather than a built-in function.
* tree.h (build_call_expr_internal_loc): Declare.
(maybe_build_call_expr_loc): Likewise.
* tree.c (build_call_expr_internal_loc_array): New function.
(maybe_build_call_expr_loc): Likewise.
2015-11-17 Richard Sandiford <richard.sandiford@arm.com>
* builtins.h (mathfn_built_in): Add a variant that takes * builtins.h (mathfn_built_in): Add a variant that takes
a combined_fn. a combined_fn.
* builtins.c: Include case-cfn-macros.h. * builtins.c: Include case-cfn-macros.h.
...@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -32,6 +32,7 @@ along with GCC; see the file COPYING3. If not see
#include "tree-dfa.h" #include "tree-dfa.h"
#include "builtins.h" #include "builtins.h"
#include "dumpfile.h" #include "dumpfile.h"
#include "case-cfn-macros.h"
/* Routine to determine if the types T1 and T2 are effectively /* Routine to determine if the types T1 and T2 are effectively
......
...@@ -230,6 +230,12 @@ enum built_in_function { ...@@ -230,6 +230,12 @@ enum built_in_function {
END_BUILTINS END_BUILTINS
}; };
#define DEF_INTERNAL_FN(CODE, FLAGS, FNSPEC) IFN_##CODE,
enum internal_fn {
#include "internal-fn.def"
IFN_LAST
};
/* Return true if CODE represents a commutative tree code. Otherwise /* Return true if CODE represents a commutative tree code. Otherwise
return false. */ return false. */
bool bool
...@@ -341,13 +347,15 @@ struct operator_id : public id_base ...@@ -341,13 +347,15 @@ struct operator_id : public id_base
const char *tcc; const char *tcc;
}; };
/* Identifier that maps to a builtin function code. */ /* Identifier that maps to a builtin or internal function code. */
struct fn_id : public id_base struct fn_id : public id_base
{ {
fn_id (enum built_in_function fn_, const char *id_) fn_id (enum built_in_function fn_, const char *id_)
: id_base (id_base::FN, id_), fn (fn_) {} : id_base (id_base::FN, id_), fn (fn_) {}
enum built_in_function fn; fn_id (enum internal_fn fn_, const char *id_)
: id_base (id_base::FN, id_), fn (int (END_BUILTINS) + int (fn_)) {}
unsigned int fn;
}; };
struct simplify; struct simplify;
...@@ -447,10 +455,12 @@ add_operator (enum tree_code code, const char *id, ...@@ -447,10 +455,12 @@ add_operator (enum tree_code code, const char *id,
*slot = op; *slot = op;
} }
/* Add a builtin identifier to the hash. */ /* Add a built-in or internal function identifier to the hash. ID is
the name of its CFN_* enumeration value. */
template <typename T>
static void static void
add_builtin (enum built_in_function code, const char *id) add_function (T code, const char *id)
{ {
fn_id *fn = new fn_id (code, id); fn_id *fn = new fn_id (code, id);
id_base **slot = operators->find_slot_with_hash (fn, fn->hashval, INSERT); id_base **slot = operators->find_slot_with_hash (fn, fn->hashval, INSERT);
...@@ -485,30 +495,32 @@ get_operator (const char *id) ...@@ -485,30 +495,32 @@ get_operator (const char *id)
return op; return op;
} }
/* Try all-uppercase. */ char *id2;
char *id2 = xstrdup (id); bool all_upper = true;
for (unsigned i = 0; i < strlen (id2); ++i) bool all_lower = true;
id2[i] = TOUPPER (id2[i]); for (unsigned int i = 0; id[i]; ++i)
new (&tem) id_base (id_base::CODE, id2); if (ISUPPER (id[i]))
op = operators->find_with_hash (&tem, tem.hashval); all_lower = false;
if (op) else if (ISLOWER (id[i]))
all_upper = false;
if (all_lower)
{ {
free (id2); /* Try in caps with _EXPR appended. */
return op; id2 = ACONCAT ((id, "_EXPR", NULL));
for (unsigned int i = 0; id2[i]; ++i)
id2[i] = TOUPPER (id2[i]);
} }
else if (all_upper && strncmp (id, "IFN_", 4) == 0)
/* Try CFN_ instead of IFN_. */
id2 = ACONCAT (("CFN_", id + 4, NULL));
else if (all_upper && strncmp (id, "BUILT_IN_", 9) == 0)
/* Try prepending CFN_. */
id2 = ACONCAT (("CFN_", id, NULL));
else
return NULL;
/* Try _EXPR appended. */
id2 = (char *)xrealloc (id2, strlen (id2) + sizeof ("_EXPR") + 1);
strcat (id2, "_EXPR");
new (&tem) id_base (id_base::CODE, id2); new (&tem) id_base (id_base::CODE, id2);
op = operators->find_with_hash (&tem, tem.hashval); return operators->find_with_hash (&tem, tem.hashval);
if (op)
{
free (id2);
return op;
}
return 0;
} }
typedef hash_map<nofree_string_hash, unsigned> cid_map_t; typedef hash_map<nofree_string_hash, unsigned> cid_map_t;
...@@ -2207,17 +2219,18 @@ expr::gen_transform (FILE *f, int indent, const char *dest, bool gimple, ...@@ -2207,17 +2219,18 @@ expr::gen_transform (FILE *f, int indent, const char *dest, bool gimple,
else else
{ {
fprintf_indent (f, indent, "{\n"); fprintf_indent (f, indent, "{\n");
fprintf_indent (f, indent, " tree decl = builtin_decl_implicit (%s);\n", fprintf_indent (f, indent, " res = maybe_build_call_expr_loc (loc, "
opr_name); "%s, %s, %d", opr_name, type, ops.length());
fprintf_indent (f, indent, " if (!decl) return NULL_TREE;\n");
fprintf_indent (f, indent, " res = build_call_expr_loc (loc, "
"decl, %d", ops.length());
} }
for (unsigned i = 0; i < ops.length (); ++i) for (unsigned i = 0; i < ops.length (); ++i)
fprintf (f, ", ops%d[%u]", depth, i); fprintf (f, ", ops%d[%u]", depth, i);
fprintf (f, ");\n"); fprintf (f, ");\n");
if (opr->kind != id_base::CODE) if (opr->kind != id_base::CODE)
fprintf_indent (f, indent, "}\n"); {
fprintf_indent (f, indent, " if (!res)\n");
fprintf_indent (f, indent, " return NULL_TREE;\n");
fprintf_indent (f, indent, "}\n");
}
if (*opr == CONVERT_EXPR) if (*opr == CONVERT_EXPR)
{ {
indent -= 2; indent -= 2;
...@@ -2665,20 +2678,14 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool gimple, ...@@ -2665,20 +2678,14 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool gimple,
if (fns_len) if (fns_len)
{ {
fprintf_indent (f, indent, fprintf_indent (f, indent,
"%sif (gimple_call_builtin_p (def_stmt, BUILT_IN_NORMAL))\n", "%sif (gcall *def = dyn_cast <gcall *>"
" (def_stmt))\n",
exprs_len ? "else " : ""); exprs_len ? "else " : "");
fprintf_indent (f, indent, fprintf_indent (f, indent,
" {\n"); " switch (gimple_call_combined_fn (def))\n");
fprintf_indent (f, indent,
" gcall *def = as_a <gcall *> (def_stmt);\n");
fprintf_indent (f, indent,
" tree fndecl = gimple_call_fndecl (def);\n");
fprintf_indent (f, indent,
" switch (DECL_FUNCTION_CODE (fndecl))\n");
fprintf_indent (f, indent,
" {\n");
indent += 6; indent += 4;
fprintf_indent (f, indent, "{\n");
for (unsigned i = 0; i < fns_len; ++i) for (unsigned i = 0; i < fns_len; ++i)
{ {
expr *e = as_a <expr *>(fns[i]->op); expr *e = as_a <expr *>(fns[i]->op);
...@@ -2691,8 +2698,7 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool gimple, ...@@ -2691,8 +2698,7 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool gimple,
fprintf_indent (f, indent, "default:;\n"); fprintf_indent (f, indent, "default:;\n");
fprintf_indent (f, indent, "}\n"); fprintf_indent (f, indent, "}\n");
indent -= 6; indent -= 4;
fprintf_indent (f, indent, " }\n");
} }
indent -= 6; indent -= 6;
...@@ -2719,17 +2725,11 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool gimple, ...@@ -2719,17 +2725,11 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool gimple,
fprintf_indent (f, indent, fprintf_indent (f, indent,
"case CALL_EXPR:\n"); "case CALL_EXPR:\n");
fprintf_indent (f, indent, fprintf_indent (f, indent,
" {\n"); " switch (get_call_combined_fn (%s))\n",
fprintf_indent (f, indent,
" tree fndecl = get_callee_fndecl (%s);\n",
kid_opname); kid_opname);
fprintf_indent (f, indent, fprintf_indent (f, indent,
" if (fndecl && DECL_BUILT_IN_CLASS (fndecl) == BUILT_IN_NORMAL)\n"); " {\n");
fprintf_indent (f, indent, indent += 4;
" switch (DECL_FUNCTION_CODE (fndecl))\n");
fprintf_indent (f, indent,
" {\n");
indent += 8;
for (unsigned j = 0; j < generic_fns.length (); ++j) for (unsigned j = 0; j < generic_fns.length (); ++j)
{ {
...@@ -2742,12 +2742,11 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool gimple, ...@@ -2742,12 +2742,11 @@ dt_node::gen_kids_1 (FILE *f, int indent, bool gimple,
fprintf_indent (f, indent, " break;\n"); fprintf_indent (f, indent, " break;\n");
fprintf_indent (f, indent, " }\n"); fprintf_indent (f, indent, " }\n");
} }
fprintf_indent (f, indent, "default:;\n");
indent -= 8; indent -= 4;
fprintf_indent (f, indent, " default:;\n"); fprintf_indent (f, indent, " }\n");
fprintf_indent (f, indent, " }\n"); fprintf_indent (f, indent, " break;\n");
fprintf_indent (f, indent, " break;\n");
fprintf_indent (f, indent, " }\n");
} }
/* Close switch (TREE_CODE ()). */ /* Close switch (TREE_CODE ()). */
...@@ -3108,24 +3107,18 @@ dt_simplify::gen_1 (FILE *f, int indent, bool gimple, operand *result) ...@@ -3108,24 +3107,18 @@ dt_simplify::gen_1 (FILE *f, int indent, bool gimple, operand *result)
*e->operation == CONVERT_EXPR *e->operation == CONVERT_EXPR
? "NOP_EXPR" : e->operation->id); ? "NOP_EXPR" : e->operation->id);
else else
{ fprintf_indent (f, indent,
fprintf_indent (f, indent, "res = maybe_build_call_expr_loc (loc, "
"{\n"); "%s, type, %d", e->operation->id,
fprintf_indent (f, indent, e->ops.length());
" tree decl = builtin_decl_implicit (%s);\n",
e->operation->id);
fprintf_indent (f, indent,
" if (!decl) return NULL_TREE;\n");
fprintf_indent (f, indent,
" res = build_call_expr_loc "
"(loc, decl, %d",
e->ops.length());
}
for (unsigned j = 0; j < e->ops.length (); ++j) for (unsigned j = 0; j < e->ops.length (); ++j)
fprintf (f, ", res_op%d", j); fprintf (f, ", res_op%d", j);
fprintf (f, ");\n"); fprintf (f, ");\n");
if (!is_a <operator_id *> (opr)) if (!is_a <operator_id *> (opr))
fprintf_indent (f, indent, "}\n"); {
fprintf_indent (f, indent, "if (!res)\n");
fprintf_indent (f, indent, " return NULL_TREE;\n");
}
} }
} }
} }
...@@ -3225,7 +3218,7 @@ dt_simplify::gen (FILE *f, int indent, bool gimple) ...@@ -3225,7 +3218,7 @@ dt_simplify::gen (FILE *f, int indent, bool gimple)
s->for_subst_vec[i].first->id, s->for_subst_vec[i].first->id,
s->for_subst_vec[i].second->id); s->for_subst_vec[i].second->id);
else if (is_a <fn_id *> (s->for_subst_vec[i].second)) else if (is_a <fn_id *> (s->for_subst_vec[i].second))
fprintf_indent (f, indent, "enum built_in_function %s = %s;\n", fprintf_indent (f, indent, "combined_fn %s = %s;\n",
s->for_subst_vec[i].first->id, s->for_subst_vec[i].first->id,
s->for_subst_vec[i].second->id); s->for_subst_vec[i].second->id);
else else
...@@ -3380,7 +3373,7 @@ decision_tree::gen (FILE *f, bool gimple) ...@@ -3380,7 +3373,7 @@ decision_tree::gen (FILE *f, bool gimple)
fprintf (f, ", enum tree_code ARG_UNUSED (%s)", fprintf (f, ", enum tree_code ARG_UNUSED (%s)",
s->s->s->for_subst_vec[i].first->id); s->s->s->for_subst_vec[i].first->id);
else if (is_a <fn_id *> (s->s->s->for_subst_vec[i].second)) else if (is_a <fn_id *> (s->s->s->for_subst_vec[i].second))
fprintf (f, ", enum built_in_function ARG_UNUSED (%s)", fprintf (f, ", combined_fn ARG_UNUSED (%s)",
s->s->s->for_subst_vec[i].first->id); s->s->s->for_subst_vec[i].first->id);
} }
...@@ -4603,12 +4596,12 @@ main (int argc, char **argv) ...@@ -4603,12 +4596,12 @@ main (int argc, char **argv)
add_operator (SYM, # SYM, # TYPE, NARGS); add_operator (SYM, # SYM, # TYPE, NARGS);
#define END_OF_BASE_TREE_CODES #define END_OF_BASE_TREE_CODES
#include "tree.def" #include "tree.def"
add_operator (CONVERT0, "CONVERT0", "tcc_unary", 1); add_operator (CONVERT0, "convert0", "tcc_unary", 1);
add_operator (CONVERT1, "CONVERT1", "tcc_unary", 1); add_operator (CONVERT1, "convert1", "tcc_unary", 1);
add_operator (CONVERT2, "CONVERT2", "tcc_unary", 1); add_operator (CONVERT2, "convert2", "tcc_unary", 1);
add_operator (VIEW_CONVERT0, "VIEW_CONVERT0", "tcc_unary", 1); add_operator (VIEW_CONVERT0, "view_convert0", "tcc_unary", 1);
add_operator (VIEW_CONVERT1, "VIEW_CONVERT1", "tcc_unary", 1); add_operator (VIEW_CONVERT1, "view_convert1", "tcc_unary", 1);
add_operator (VIEW_CONVERT2, "VIEW_CONVERT2", "tcc_unary", 1); add_operator (VIEW_CONVERT2, "view_convert2", "tcc_unary", 1);
#undef END_OF_BASE_TREE_CODES #undef END_OF_BASE_TREE_CODES
#undef DEFTREECODE #undef DEFTREECODE
...@@ -4616,9 +4609,13 @@ add_operator (VIEW_CONVERT2, "VIEW_CONVERT2", "tcc_unary", 1); ...@@ -4616,9 +4609,13 @@ add_operator (VIEW_CONVERT2, "VIEW_CONVERT2", "tcc_unary", 1);
??? Cannot use N (name) as that is targetm.emultls.get_address ??? Cannot use N (name) as that is targetm.emultls.get_address
for BUILT_IN_EMUTLS_GET_ADDRESS ... */ for BUILT_IN_EMUTLS_GET_ADDRESS ... */
#define DEF_BUILTIN(ENUM, N, C, T, LT, B, F, NA, AT, IM, COND) \ #define DEF_BUILTIN(ENUM, N, C, T, LT, B, F, NA, AT, IM, COND) \
add_builtin (ENUM, # ENUM); add_function (ENUM, "CFN_" # ENUM);
#include "builtins.def" #include "builtins.def"
#define DEF_INTERNAL_FN(CODE, NAME, FNSPEC) \
add_function (IFN_##CODE, "CFN_" #CODE);
#include "internal-fn.def"
/* Parse ahead! */ /* Parse ahead! */
parser p (r); parser p (r);
......
...@@ -3369,7 +3369,7 @@ replace_stmt_with_simplification (gimple_stmt_iterator *gsi, ...@@ -3369,7 +3369,7 @@ replace_stmt_with_simplification (gimple_stmt_iterator *gsi,
} }
} }
else if (rcode.is_fn_code () else if (rcode.is_fn_code ()
&& gimple_call_builtin_p (stmt, rcode)) && gimple_call_combined_fn (stmt) == rcode)
{ {
unsigned i; unsigned i;
for (i = 0; i < gimple_call_num_args (stmt); ++i) for (i = 0; i < gimple_call_num_args (stmt); ++i)
......
...@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3. If not see ...@@ -28,6 +28,7 @@ along with GCC; see the file COPYING3. If not see
#include "ssa.h" #include "ssa.h"
#include "cgraph.h" #include "cgraph.h"
#include "fold-const.h" #include "fold-const.h"
#include "fold-const-call.h"
#include "stor-layout.h" #include "stor-layout.h"
#include "gimple-fold.h" #include "gimple-fold.h"
#include "calls.h" #include "calls.h"
...@@ -35,6 +36,8 @@ along with GCC; see the file COPYING3. If not see ...@@ -35,6 +36,8 @@ along with GCC; see the file COPYING3. If not see
#include "builtins.h" #include "builtins.h"
#include "gimple-match.h" #include "gimple-match.h"
#include "tree-pass.h" #include "tree-pass.h"
#include "internal-fn.h"
#include "case-cfn-macros.h"
/* Forward declarations of the private auto-generated matchers. /* Forward declarations of the private auto-generated matchers.
...@@ -81,19 +84,7 @@ gimple_resimplify1 (gimple_seq *seq, ...@@ -81,19 +84,7 @@ gimple_resimplify1 (gimple_seq *seq,
if (res_code->is_tree_code ()) if (res_code->is_tree_code ())
tem = const_unop (*res_code, type, res_ops[0]); tem = const_unop (*res_code, type, res_ops[0]);
else else
{ tem = fold_const_call (combined_fn (*res_code), type, res_ops[0]);
tree decl = builtin_decl_implicit (*res_code);
if (decl)
{
tem = fold_builtin_n (UNKNOWN_LOCATION, decl, res_ops, 1, false);
if (tem)
{
/* fold_builtin_n wraps the result inside a NOP_EXPR. */
STRIP_NOPS (tem);
tem = fold_convert (type, tem);
}
}
}
if (tem != NULL_TREE if (tem != NULL_TREE
&& CONSTANT_CLASS_P (tem)) && CONSTANT_CLASS_P (tem))
{ {
...@@ -137,19 +128,8 @@ gimple_resimplify2 (gimple_seq *seq, ...@@ -137,19 +128,8 @@ gimple_resimplify2 (gimple_seq *seq,
if (res_code->is_tree_code ()) if (res_code->is_tree_code ())
tem = const_binop (*res_code, type, res_ops[0], res_ops[1]); tem = const_binop (*res_code, type, res_ops[0], res_ops[1]);
else else
{ tem = fold_const_call (combined_fn (*res_code), type,
tree decl = builtin_decl_implicit (*res_code); res_ops[0], res_ops[1]);
if (decl)
{
tem = fold_builtin_n (UNKNOWN_LOCATION, decl, res_ops, 2, false);
if (tem)
{
/* fold_builtin_n wraps the result inside a NOP_EXPR. */
STRIP_NOPS (tem);
tem = fold_convert (type, tem);
}
}
}
if (tem != NULL_TREE if (tem != NULL_TREE
&& CONSTANT_CLASS_P (tem)) && CONSTANT_CLASS_P (tem))
{ {
...@@ -208,19 +188,8 @@ gimple_resimplify3 (gimple_seq *seq, ...@@ -208,19 +188,8 @@ gimple_resimplify3 (gimple_seq *seq,
tem = fold_ternary/*_to_constant*/ (*res_code, type, res_ops[0], tem = fold_ternary/*_to_constant*/ (*res_code, type, res_ops[0],
res_ops[1], res_ops[2]); res_ops[1], res_ops[2]);
else else
{ tem = fold_const_call (combined_fn (*res_code), type,
tree decl = builtin_decl_implicit (*res_code); res_ops[0], res_ops[1], res_ops[2]);
if (decl)
{
tem = fold_builtin_n (UNKNOWN_LOCATION, decl, res_ops, 3, false);
if (tem)
{
/* fold_builtin_n wraps the result inside a NOP_EXPR. */
STRIP_NOPS (tem);
tem = fold_convert (type, tem);
}
}
}
if (tem != NULL_TREE if (tem != NULL_TREE
&& CONSTANT_CLASS_P (tem)) && CONSTANT_CLASS_P (tem))
{ {
...@@ -282,6 +251,22 @@ maybe_build_generic_op (enum tree_code code, tree type, ...@@ -282,6 +251,22 @@ maybe_build_generic_op (enum tree_code code, tree type,
tree (*mprts_hook) (code_helper, tree, tree *); tree (*mprts_hook) (code_helper, tree, tree *);
/* Try to build a call to FN with return type TYPE and the NARGS
arguments given in OPS. Return null if the target doesn't support
the function. */
static gcall *
build_call_internal (internal_fn fn, tree type, unsigned int nargs, tree *ops)
{
if (direct_internal_fn_p (fn))
{
tree_pair types = direct_internal_fn_types (fn, type, ops);
if (!direct_internal_fn_supported_p (fn, types))
return NULL;
}
return gimple_build_call_internal (fn, nargs, ops[0], ops[1], ops[2]);
}
/* Push the exploded expression described by RCODE, TYPE and OPS /* Push the exploded expression described by RCODE, TYPE and OPS
as a statement to SEQ if necessary and return a gimple value as a statement to SEQ if necessary and return a gimple value
denoting the value of the expression. If RES is not NULL denoting the value of the expression. If RES is not NULL
...@@ -333,12 +318,7 @@ maybe_push_res_to_seq (code_helper rcode, tree type, tree *ops, ...@@ -333,12 +318,7 @@ maybe_push_res_to_seq (code_helper rcode, tree type, tree *ops,
{ {
if (!seq) if (!seq)
return NULL_TREE; return NULL_TREE;
tree decl = builtin_decl_implicit (rcode); combined_fn fn = rcode;
if (!decl)
return NULL_TREE;
/* We can't and should not emit calls to non-const functions. */
if (!(flags_from_decl_or_type (decl) & ECF_CONST))
return NULL_TREE;
/* Play safe and do not allow abnormals to be mentioned in /* Play safe and do not allow abnormals to be mentioned in
newly created statements. */ newly created statements. */
unsigned nargs; unsigned nargs;
...@@ -351,6 +331,28 @@ maybe_push_res_to_seq (code_helper rcode, tree type, tree *ops, ...@@ -351,6 +331,28 @@ maybe_push_res_to_seq (code_helper rcode, tree type, tree *ops,
return NULL_TREE; return NULL_TREE;
} }
gcc_assert (nargs != 0); gcc_assert (nargs != 0);
gcall *new_stmt = NULL;
if (internal_fn_p (fn))
{
/* Generate the given function if we can. */
internal_fn ifn = as_internal_fn (fn);
new_stmt = build_call_internal (ifn, type, nargs, ops);
if (!new_stmt)
return NULL_TREE;
}
else
{
/* Find the function we want to call. */
tree decl = builtin_decl_implicit (as_builtin_fn (fn));
if (!decl)
return NULL;
/* We can't and should not emit calls to non-const functions. */
if (!(flags_from_decl_or_type (decl) & ECF_CONST))
return NULL;
new_stmt = gimple_build_call (decl, nargs, ops[0], ops[1], ops[2]);
}
if (!res) if (!res)
{ {
if (gimple_in_ssa_p (cfun)) if (gimple_in_ssa_p (cfun))
...@@ -358,7 +360,6 @@ maybe_push_res_to_seq (code_helper rcode, tree type, tree *ops, ...@@ -358,7 +360,6 @@ maybe_push_res_to_seq (code_helper rcode, tree type, tree *ops,
else else
res = create_tmp_reg (type); res = create_tmp_reg (type);
} }
gimple *new_stmt = gimple_build_call (decl, nargs, ops[0], ops[1], ops[2]);
gimple_call_set_lhs (new_stmt, res); gimple_call_set_lhs (new_stmt, res);
gimple_seq_add_stmt_without_update (seq, new_stmt); gimple_seq_add_stmt_without_update (seq, new_stmt);
return res; return res;
...@@ -471,25 +472,15 @@ gimple_simplify (enum built_in_function fn, tree type, ...@@ -471,25 +472,15 @@ gimple_simplify (enum built_in_function fn, tree type,
{ {
if (constant_for_folding (arg0)) if (constant_for_folding (arg0))
{ {
tree decl = builtin_decl_implicit (fn); tree res = fold_const_call (as_combined_fn (fn), type, arg0);
if (decl) if (res && CONSTANT_CLASS_P (res))
{ return res;
tree res = fold_builtin_n (UNKNOWN_LOCATION, decl, &arg0, 1, false);
if (res)
{
/* fold_builtin_n wraps the result inside a NOP_EXPR. */
STRIP_NOPS (res);
res = fold_convert (type, res);
if (CONSTANT_CLASS_P (res))
return res;
}
}
} }
code_helper rcode; code_helper rcode;
tree ops[3] = {}; tree ops[3] = {};
if (!gimple_simplify (&rcode, ops, seq, valueize, if (!gimple_simplify (&rcode, ops, seq, valueize,
fn, type, arg0)) as_combined_fn (fn), type, arg0))
return NULL_TREE; return NULL_TREE;
return maybe_push_res_to_seq (rcode, type, ops, seq); return maybe_push_res_to_seq (rcode, type, ops, seq);
} }
...@@ -504,28 +495,15 @@ gimple_simplify (enum built_in_function fn, tree type, ...@@ -504,28 +495,15 @@ gimple_simplify (enum built_in_function fn, tree type,
if (constant_for_folding (arg0) if (constant_for_folding (arg0)
&& constant_for_folding (arg1)) && constant_for_folding (arg1))
{ {
tree decl = builtin_decl_implicit (fn); tree res = fold_const_call (as_combined_fn (fn), type, arg0, arg1);
if (decl) if (res && CONSTANT_CLASS_P (res))
{ return res;
tree args[2];
args[0] = arg0;
args[1] = arg1;
tree res = fold_builtin_n (UNKNOWN_LOCATION, decl, args, 2, false);
if (res)
{
/* fold_builtin_n wraps the result inside a NOP_EXPR. */
STRIP_NOPS (res);
res = fold_convert (type, res);
if (CONSTANT_CLASS_P (res))
return res;
}
}
} }
code_helper rcode; code_helper rcode;
tree ops[3] = {}; tree ops[3] = {};
if (!gimple_simplify (&rcode, ops, seq, valueize, if (!gimple_simplify (&rcode, ops, seq, valueize,
fn, type, arg0, arg1)) as_combined_fn (fn), type, arg0, arg1))
return NULL_TREE; return NULL_TREE;
return maybe_push_res_to_seq (rcode, type, ops, seq); return maybe_push_res_to_seq (rcode, type, ops, seq);
} }
...@@ -541,29 +519,15 @@ gimple_simplify (enum built_in_function fn, tree type, ...@@ -541,29 +519,15 @@ gimple_simplify (enum built_in_function fn, tree type,
&& constant_for_folding (arg1) && constant_for_folding (arg1)
&& constant_for_folding (arg2)) && constant_for_folding (arg2))
{ {
tree decl = builtin_decl_implicit (fn); tree res = fold_const_call (as_combined_fn (fn), type, arg0, arg1, arg2);
if (decl) if (res && CONSTANT_CLASS_P (res))
{ return res;
tree args[3];
args[0] = arg0;
args[1] = arg1;
args[2] = arg2;
tree res = fold_builtin_n (UNKNOWN_LOCATION, decl, args, 3, false);
if (res)
{
/* fold_builtin_n wraps the result inside a NOP_EXPR. */
STRIP_NOPS (res);
res = fold_convert (type, res);
if (CONSTANT_CLASS_P (res))
return res;
}
}
} }
code_helper rcode; code_helper rcode;
tree ops[3] = {}; tree ops[3] = {};
if (!gimple_simplify (&rcode, ops, seq, valueize, if (!gimple_simplify (&rcode, ops, seq, valueize,
fn, type, arg0, arg1, arg2)) as_combined_fn (fn), type, arg0, arg1, arg2))
return NULL_TREE; return NULL_TREE;
return maybe_push_res_to_seq (rcode, type, ops, seq); return maybe_push_res_to_seq (rcode, type, ops, seq);
} }
...@@ -726,23 +690,29 @@ gimple_simplify (gimple *stmt, ...@@ -726,23 +690,29 @@ gimple_simplify (gimple *stmt,
&& gimple_call_num_args (stmt) >= 1 && gimple_call_num_args (stmt) >= 1
&& gimple_call_num_args (stmt) <= 3) && gimple_call_num_args (stmt) <= 3)
{ {
tree fn = gimple_call_fn (stmt);
/* ??? Internal function support missing. */
if (!fn)
return false;
bool valueized = false; bool valueized = false;
fn = do_valueize (fn, top_valueize, valueized); if (gimple_call_internal_p (stmt))
if (TREE_CODE (fn) != ADDR_EXPR *rcode = as_combined_fn (gimple_call_internal_fn (stmt));
|| TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL) else
return false; {
tree fn = gimple_call_fn (stmt);
if (!fn)
return false;
tree decl = TREE_OPERAND (fn, 0); fn = do_valueize (fn, top_valueize, valueized);
if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL if (TREE_CODE (fn) != ADDR_EXPR
|| !gimple_builtin_call_types_compatible_p (stmt, decl)) || TREE_CODE (TREE_OPERAND (fn, 0)) != FUNCTION_DECL)
return false; return false;
tree decl = TREE_OPERAND (fn, 0);
if (DECL_BUILT_IN_CLASS (decl) != BUILT_IN_NORMAL
|| !gimple_builtin_call_types_compatible_p (stmt, decl))
return false;
*rcode = as_combined_fn (DECL_FUNCTION_CODE (decl));
}
tree type = TREE_TYPE (gimple_call_lhs (stmt)); tree type = TREE_TYPE (gimple_call_lhs (stmt));
*rcode = DECL_FUNCTION_CODE (decl);
for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i) for (unsigned i = 0; i < gimple_call_num_args (stmt); ++i)
{ {
tree arg = gimple_call_arg (stmt, i); tree arg = gimple_call_arg (stmt, i);
......
...@@ -30,9 +30,9 @@ class code_helper ...@@ -30,9 +30,9 @@ class code_helper
public: public:
code_helper () {} code_helper () {}
code_helper (tree_code code) : rep ((int) code) {} code_helper (tree_code code) : rep ((int) code) {}
code_helper (built_in_function fn) : rep (-(int) fn) {} code_helper (combined_fn fn) : rep (-(int) fn) {}
operator tree_code () const { return (tree_code) rep; } operator tree_code () const { return (tree_code) rep; }
operator built_in_function () const { return (built_in_function) -rep; } operator combined_fn () const { return (combined_fn) -rep; }
bool is_tree_code () const { return rep > 0; } bool is_tree_code () const { return rep > 0; }
bool is_fn_code () const { return rep < 0; } bool is_fn_code () const { return rep < 0; }
int get_rep () const { return rep; } int get_rep () const { return rep; }
......
...@@ -2510,32 +2510,33 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) ...@@ -2510,32 +2510,33 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
exps (EXP2 EXP10 POW10 EXP EXP10 POW10 EXP EXP2) exps (EXP2 EXP10 POW10 EXP EXP10 POW10 EXP EXP2)
(simplify (simplify
(logs (exps @0)) (logs (exps @0))
(with { (if (SCALAR_FLOAT_TYPE_P (type))
tree x; (with {
switch (exps) tree x;
{ switch (exps)
CASE_FLT_FN (BUILT_IN_EXP): {
/* Prepare to do logN(exp(exponent)) -> exponent*logN(e). */ CASE_CFN_EXP:
x = build_real_truncate (type, dconst_e ()); /* Prepare to do logN(exp(exponent)) -> exponent*logN(e). */
break; x = build_real_truncate (type, dconst_e ());
CASE_FLT_FN (BUILT_IN_EXP2): break;
/* Prepare to do logN(exp2(exponent)) -> exponent*logN(2). */ CASE_CFN_EXP2:
x = build_real (type, dconst2); /* Prepare to do logN(exp2(exponent)) -> exponent*logN(2). */
break; x = build_real (type, dconst2);
CASE_FLT_FN (BUILT_IN_EXP10): break;
CASE_FLT_FN (BUILT_IN_POW10): CASE_CFN_EXP10:
/* Prepare to do logN(exp10(exponent)) -> exponent*logN(10). */ CASE_CFN_POW10:
{ /* Prepare to do logN(exp10(exponent)) -> exponent*logN(10). */
REAL_VALUE_TYPE dconst10; {
real_from_integer (&dconst10, VOIDmode, 10, SIGNED); REAL_VALUE_TYPE dconst10;
x = build_real (type, dconst10); real_from_integer (&dconst10, VOIDmode, 10, SIGNED);
} x = build_real (type, dconst10);
break; }
default: break;
gcc_unreachable (); default:
} gcc_unreachable ();
} }
(mult (logs { x; }) @0)))) }
(mult (logs { x; }) @0)))))
(for logs (LOG LOG (for logs (LOG LOG
LOG2 LOG2 LOG2 LOG2
...@@ -2543,23 +2544,24 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) ...@@ -2543,23 +2544,24 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
exps (SQRT CBRT) exps (SQRT CBRT)
(simplify (simplify
(logs (exps @0)) (logs (exps @0))
(with { (if (SCALAR_FLOAT_TYPE_P (type))
tree x; (with {
switch (exps) tree x;
{ switch (exps)
CASE_FLT_FN (BUILT_IN_SQRT): {
/* Prepare to do logN(sqrt(x)) -> 0.5*logN(x). */ CASE_CFN_SQRT:
x = build_real (type, dconsthalf); /* Prepare to do logN(sqrt(x)) -> 0.5*logN(x). */
break; x = build_real (type, dconsthalf);
CASE_FLT_FN (BUILT_IN_CBRT): break;
/* Prepare to do logN(cbrt(x)) -> (1/3)*logN(x). */ CASE_CFN_CBRT:
x = build_real_truncate (type, dconst_third ()); /* Prepare to do logN(cbrt(x)) -> (1/3)*logN(x). */
break; x = build_real_truncate (type, dconst_third ());
default: break;
gcc_unreachable (); default:
} gcc_unreachable ();
} }
(mult { x; } (logs @0))))) }
(mult { x; } (logs @0))))))
/* logN(pow(x,exponent)) -> exponent*logN(x). */ /* logN(pow(x,exponent)) -> exponent*logN(x). */
(for logs (LOG LOG2 LOG10) (for logs (LOG LOG2 LOG10)
...@@ -2616,7 +2618,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) ...@@ -2616,7 +2618,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
/* hypot(x,0) and hypot(0,x) -> abs(x). */ /* hypot(x,0) and hypot(0,x) -> abs(x). */
(simplify (simplify
(hypot:c @0 real_zerop@1) (HYPOT:c @0 real_zerop@1)
(abs @0)) (abs @0))
/* pow(1,x) -> 1. */ /* pow(1,x) -> 1. */
...@@ -2684,7 +2686,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) ...@@ -2684,7 +2686,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
(rdiv (SIN:s @0) (TAN:s @0)) (rdiv (SIN:s @0) (TAN:s @0))
(if (! HONOR_NANS (@0) (if (! HONOR_NANS (@0)
&& ! HONOR_INFINITIES (@0)) && ! HONOR_INFINITIES (@0))
(cos @0))) (COS @0)))
/* Simplify tan(x) / sin(x) -> 1.0 / cos(x). */ /* Simplify tan(x) / sin(x) -> 1.0 / cos(x). */
(simplify (simplify
......
...@@ -11063,6 +11063,22 @@ build_call_expr (tree fndecl, int n, ...) ...@@ -11063,6 +11063,22 @@ build_call_expr (tree fndecl, int n, ...)
return build_call_expr_loc_array (UNKNOWN_LOCATION, fndecl, n, argarray); return build_call_expr_loc_array (UNKNOWN_LOCATION, fndecl, n, argarray);
} }
/* Build an internal call to IFN, with arguments ARGS[0:N-1] and with return
type TYPE. This is just like CALL_EXPR, except its CALL_EXPR_FN is NULL.
It will get gimplified later into an ordinary internal function. */
tree
build_call_expr_internal_loc_array (location_t loc, internal_fn ifn,
tree type, int n, const tree *args)
{
tree t = build_call_1 (type, NULL_TREE, n);
for (int i = 0; i < n; ++i)
CALL_EXPR_ARG (t, i) = args[i];
SET_EXPR_LOCATION (t, loc);
CALL_EXPR_IFN (t) = ifn;
return t;
}
/* Build internal call expression. This is just like CALL_EXPR, except /* Build internal call expression. This is just like CALL_EXPR, except
its CALL_EXPR_FN is NULL. It will get gimplified later into ordinary its CALL_EXPR_FN is NULL. It will get gimplified later into ordinary
internal function. */ internal function. */
...@@ -11072,16 +11088,52 @@ build_call_expr_internal_loc (location_t loc, enum internal_fn ifn, ...@@ -11072,16 +11088,52 @@ build_call_expr_internal_loc (location_t loc, enum internal_fn ifn,
tree type, int n, ...) tree type, int n, ...)
{ {
va_list ap; va_list ap;
tree *argarray = XALLOCAVEC (tree, n);
int i; int i;
tree fn = build_call_1 (type, NULL_TREE, n);
va_start (ap, n); va_start (ap, n);
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
CALL_EXPR_ARG (fn, i) = va_arg (ap, tree); argarray[i] = va_arg (ap, tree);
va_end (ap); va_end (ap);
SET_EXPR_LOCATION (fn, loc); return build_call_expr_internal_loc_array (loc, ifn, type, n, argarray);
CALL_EXPR_IFN (fn) = ifn; }
return fn;
/* Return a function call to FN, if the target is guaranteed to support it,
or null otherwise.
N is the number of arguments, passed in the "...", and TYPE is the
type of the return value. */
tree
maybe_build_call_expr_loc (location_t loc, combined_fn fn, tree type,
int n, ...)
{
va_list ap;
tree *argarray = XALLOCAVEC (tree, n);
int i;
va_start (ap, n);
for (i = 0; i < n; i++)
argarray[i] = va_arg (ap, tree);
va_end (ap);
if (internal_fn_p (fn))
{
internal_fn ifn = as_internal_fn (fn);
if (direct_internal_fn_p (ifn))
{
tree_pair types = direct_internal_fn_types (ifn, type, argarray);
if (!direct_internal_fn_supported_p (ifn, types))
return NULL_TREE;
}
return build_call_expr_internal_loc_array (loc, ifn, type, n, argarray);
}
else
{
tree fndecl = builtin_decl_implicit (as_builtin_fn (fn));
if (!fndecl)
return NULL_TREE;
return build_call_expr_loc_array (loc, fndecl, n, argarray);
}
} }
/* Create a new constant string literal and return a char* pointer to it. /* Create a new constant string literal and return a char* pointer to it.
......
...@@ -3957,6 +3957,10 @@ extern tree build_call_expr_loc (location_t, tree, int, ...); ...@@ -3957,6 +3957,10 @@ extern tree build_call_expr_loc (location_t, tree, int, ...);
extern tree build_call_expr (tree, int, ...); extern tree build_call_expr (tree, int, ...);
extern tree build_call_expr_internal_loc (location_t, enum internal_fn, extern tree build_call_expr_internal_loc (location_t, enum internal_fn,
tree, int, ...); tree, int, ...);
extern tree build_call_expr_internal_loc (location_t, enum internal_fn,
tree, int, tree *);
extern tree maybe_build_call_expr_loc (location_t, combined_fn, tree,
int, ...);
extern tree build_string_literal (int, const char *); extern tree build_string_literal (int, const char *);
/* Construct various nodes representing data types. */ /* Construct various nodes representing data types. */
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment