summaryrefslogtreecommitdiff
path: root/gcc/gimple.c
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/gimple.c')
-rw-r--r--gcc/gimple.c123
1 files changed, 103 insertions, 20 deletions
diff --git a/gcc/gimple.c b/gcc/gimple.c
index c6868befc59..909091bcfd0 100644
--- a/gcc/gimple.c
+++ b/gcc/gimple.c
@@ -277,6 +277,59 @@ gimple_build_call (tree fn, unsigned nargs, ...)
}
+/* Helper for gimple_build_call_internal and gimple_build_call_internal_vec.
+ Build the basic components of a GIMPLE_CALL statement to internal
+ function FN with NARGS arguments. */
+
+static inline gimple
+gimple_build_call_internal_1 (enum internal_fn fn, unsigned nargs)
+{
+ gimple s = gimple_build_with_ops (GIMPLE_CALL, ERROR_MARK, nargs + 3);
+ s->gsbase.subcode |= GF_CALL_INTERNAL;
+ gimple_call_set_internal_fn (s, fn);
+ gimple_call_reset_alias_info (s);
+ return s;
+}
+
+
+/* Build a GIMPLE_CALL statement to internal function FN. NARGS is
+ the number of arguments. The ... are the arguments. */
+
+gimple
+gimple_build_call_internal (enum internal_fn fn, unsigned nargs, ...)
+{
+ va_list ap;
+ gimple call;
+ unsigned i;
+
+ call = gimple_build_call_internal_1 (fn, nargs);
+ va_start (ap, nargs);
+ for (i = 0; i < nargs; i++)
+ gimple_call_set_arg (call, i, va_arg (ap, tree));
+ va_end (ap);
+
+ return call;
+}
+
+
+/* Build a GIMPLE_CALL statement to internal function FN with the arguments
+ specified in vector ARGS. */
+
+gimple
+gimple_build_call_internal_vec (enum internal_fn fn, VEC(tree, heap) *args)
+{
+ unsigned i, nargs;
+ gimple call;
+
+ nargs = VEC_length (tree, args);
+ call = gimple_build_call_internal_1 (fn, nargs);
+ for (i = 0; i < nargs; i++)
+ gimple_call_set_arg (call, i, VEC_index (tree, args, i));
+
+ return call;
+}
+
+
/* Build a GIMPLE_CALL statement from CALL_EXPR T. Note that T is
assumed to be in GIMPLE form already. Minimal checking is done of
this fact. */
@@ -1778,6 +1831,20 @@ gimple_has_body_p (tree fndecl)
return (gimple_body (fndecl) || (fn && fn->cfg));
}
+/* Return true if calls C1 and C2 are known to go to the same function. */
+
+bool
+gimple_call_same_target_p (const_gimple c1, const_gimple c2)
+{
+ if (gimple_call_internal_p (c1))
+ return (gimple_call_internal_p (c2)
+ && gimple_call_internal_fn (c1) == gimple_call_internal_fn (c2));
+ else
+ return (gimple_call_fn (c1) == gimple_call_fn (c2)
+ || (gimple_call_fndecl (c1)
+ && gimple_call_fndecl (c1) == gimple_call_fndecl (c2)));
+}
+
/* Detect flags from a GIMPLE_CALL. This is just like
call_expr_flags, but for gimple tuples. */
@@ -1789,6 +1856,8 @@ gimple_call_flags (const_gimple stmt)
if (decl)
flags = flags_from_decl_or_type (decl);
+ else if (gimple_call_internal_p (stmt))
+ flags = internal_fn_flags (gimple_call_internal_fn (stmt));
else
flags = flags_from_decl_or_type (gimple_call_fntype (stmt));
@@ -1798,18 +1867,32 @@ gimple_call_flags (const_gimple stmt)
return flags;
}
+/* Return the "fn spec" string for call STMT. */
+
+static tree
+gimple_call_fnspec (const_gimple stmt)
+{
+ tree type, attr;
+
+ type = gimple_call_fntype (stmt);
+ if (!type)
+ return NULL_TREE;
+
+ attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type));
+ if (!attr)
+ return NULL_TREE;
+
+ return TREE_VALUE (TREE_VALUE (attr));
+}
+
/* Detects argument flags for argument number ARG on call STMT. */
int
gimple_call_arg_flags (const_gimple stmt, unsigned arg)
{
- tree type = gimple_call_fntype (stmt);
- tree attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type));
- if (!attr)
- return 0;
+ tree attr = gimple_call_fnspec (stmt);
- attr = TREE_VALUE (TREE_VALUE (attr));
- if (1 + arg >= (unsigned) TREE_STRING_LENGTH (attr))
+ if (!attr || 1 + arg >= (unsigned) TREE_STRING_LENGTH (attr))
return 0;
switch (TREE_STRING_POINTER (attr)[1 + arg])
@@ -1841,19 +1924,13 @@ gimple_call_arg_flags (const_gimple stmt, unsigned arg)
int
gimple_call_return_flags (const_gimple stmt)
{
- tree type;
- tree attr = NULL_TREE;
+ tree attr;
if (gimple_call_flags (stmt) & ECF_MALLOC)
return ERF_NOALIAS;
- type = gimple_call_fntype (stmt);
- attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type));
- if (!attr)
- return 0;
-
- attr = TREE_VALUE (TREE_VALUE (attr));
- if (TREE_STRING_LENGTH (attr) < 1)
+ attr = gimple_call_fnspec (stmt);
+ if (!attr || TREE_STRING_LENGTH (attr) < 1)
return 0;
switch (TREE_STRING_POINTER (attr)[0])
@@ -2278,6 +2355,7 @@ gimple_has_side_effects (const_gimple s)
if (is_gimple_call (s))
{
unsigned nargs = gimple_call_num_args (s);
+ tree fn;
if (!(gimple_call_flags (s) & (ECF_CONST | ECF_PURE)))
return true;
@@ -2292,7 +2370,8 @@ gimple_has_side_effects (const_gimple s)
return true;
}
- if (TREE_SIDE_EFFECTS (gimple_call_fn (s)))
+ fn = gimple_call_fn (s);
+ if (fn && TREE_SIDE_EFFECTS (fn))
return true;
for (i = 0; i < nargs; i++)
@@ -2331,14 +2410,15 @@ gimple_rhs_has_side_effects (const_gimple s)
if (is_gimple_call (s))
{
unsigned nargs = gimple_call_num_args (s);
+ tree fn;
if (!(gimple_call_flags (s) & (ECF_CONST | ECF_PURE)))
return true;
/* We cannot use gimple_has_volatile_ops here,
because we must ignore a volatile LHS. */
- if (TREE_SIDE_EFFECTS (gimple_call_fn (s))
- || TREE_THIS_VOLATILE (gimple_call_fn (s)))
+ fn = gimple_call_fn (s);
+ if (fn && (TREE_SIDE_EFFECTS (fn) || TREE_THIS_VOLATILE (fn)))
{
gcc_assert (gimple_has_volatile_ops (s));
return true;
@@ -3094,7 +3174,6 @@ gimple
gimple_call_copy_skip_args (gimple stmt, bitmap args_to_skip)
{
int i;
- tree fn = gimple_call_fn (stmt);
int nargs = gimple_call_num_args (stmt);
VEC(tree, heap) *vargs = VEC_alloc (tree, heap, nargs);
gimple new_stmt;
@@ -3103,7 +3182,11 @@ gimple_call_copy_skip_args (gimple stmt, bitmap args_to_skip)
if (!bitmap_bit_p (args_to_skip, i))
VEC_quick_push (tree, vargs, gimple_call_arg (stmt, i));
- new_stmt = gimple_build_call_vec (fn, vargs);
+ if (gimple_call_internal_p (stmt))
+ new_stmt = gimple_build_call_internal_vec (gimple_call_internal_fn (stmt),
+ vargs);
+ else
+ new_stmt = gimple_build_call_vec (gimple_call_fn (stmt), vargs);
VEC_free (tree, heap, vargs);
if (gimple_call_lhs (stmt))
gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt));