+2011-04-20 Richard Sandiford <richard.sandiford@linaro.org>
+
+ * Makefile.in (INTERNAL_FN_DEF, INTERNAL_FN_H): Define.
+ (GIMPLE_H): Include $(INTERNAL_FN_H).
+ (OBJS-common): Add internal-fn.o.
+ (internal-fn.o): New rule.
+ * internal-fn.def: New file.
+ * internal-fn.h: Likewise.
+ * internal-fn.c: Likewise.
+ * gimple.h: Include internal-fn.h.
+ (GF_CALL_INTERNAL): New gf_mask.
+ (gimple_statement_call): Put fntype into a union with a new
+ internal_fn field.
+ (gimple_build_call_internal): Declare.
+ (gimple_build_call_internal_vec): Likewise.
+ (gimple_call_same_target_p): Likewise.
+ (gimple_call_internal_p): New function.
+ (gimple_call_internal_fn): Likewise.
+ (gimple_call_fntype): Return null for internal calls.
+ (gimple_call_set_fntype): Assert that the function is not internal.
+ (gimple_call_set_fn): Likewise.
+ (gimple_call_set_fndecl): Likewise.
+ (gimple_call_set_internal_fn): New function.
+ (gimple_call_addr_fndecl): Handle null functions.
+ (gimple_call_return_type): Likewise null types.
+ * gimple.c (gimple_build_call_internal_1): New function.
+ (gimple_build_call_internal): Likewise.
+ (gimple_build_call_internal_vec): Likewise.
+ (gimple_call_same_target_p): Likewise.
+ (gimple_call_flags): Handle calls to internal functions.
+ (gimple_call_fnspec): New function.
+ (gimple_call_arg_flags, gimple_call_return_flags): Use it.
+ (gimple_has_side_effects): Handle null functions.
+ (gimple_rhs_has_side_effects): Likewise.
+ (gimple_call_copy_skip_args): Handle calls to internal functions.
+ * cfgexpand.c (expand_call_stmt): Likewise.
+ * expr.c (expand_expr_real_1): Assert that the call isn't internal.
+ * gimple-fold.c (gimple_fold_call): Handle null functions.
+ (gimple_fold_stmt_to_constant_1): Don't fold
+ calls to internal functions.
+ * gimple-low.c (gimple_check_call_args): Handle calls to internal
+ functions.
+ * gimple-pretty-print.c (dump_gimple_call): Likewise.
+ * ipa-prop.c (ipa_analyze_call_uses): Handle null functions.
+ * tree-cfg.c (verify_gimple_call): Handle calls to internal functions.
+ (do_warn_unused_result): Likewise.
+ * tree-eh.c (same_handler_p): Use gimple_call_same_target_p.
+ * tree-ssa-ccp.c (ccp_fold_stmt): Handle calls to internal functions.
+ * tree-ssa-dom.c (hashable_expr): Use the gimple statement to record
+ the target of a call.
+ (initialize_hash_element): Update accordingly.
+ (hashable_expr_equal_p): Use gimple_call_same_target_p.
+ (iterative_hash_hashable_expr): Handle calls to internal functions.
+ (print_expr_hash_elt): Likewise.
+ * tree-ssa-pre.c (can_value_number_call): Likewise.
+ (eliminate): Handle null functions.
+ * tree-ssa-sccvn.c (visit_use): Handle calls to internal functions.
+ * tree-ssa-structalias.c (get_fi_for_callee): Likewise.
+ (find_func_aliases): Likewise.
+ * value-prof.c (gimple_ic_transform): Likewise.
+ (gimple_indirect_call_to_profile): Likewise.
+ * lto-streamer-in.c (input_gimple_stmt): Likewise.
+ * lto-streamer-out.c (output_gimple_stmt): Likewise.
+
2011-04-19 Jan Hubicka <jh@suse.cz>
* ipa-inline-transform.c (save_inline_function_body): Add comments.
READ_MD_H = $(OBSTACK_H) $(HASHTAB_H) read-md.h
PARAMS_H = params.h params.def
BUILTINS_DEF = builtins.def sync-builtins.def omp-builtins.def
+INTERNAL_FN_DEF = internal-fn.def
+INTERNAL_FN_H = internal-fn.h $(INTERNAL_FN_DEF)
TREE_H = tree.h all-tree.def tree.def c-family/c-common.def \
$(lang_tree_files) $(MACHMODE_H) tree-check.h $(BUILTINS_DEF) \
$(INPUT_H) statistics.h $(VEC_H) treestruct.def $(HASHTAB_H) \
REGSET_H = regset.h $(BITMAP_H) hard-reg-set.h
BASIC_BLOCK_H = basic-block.h $(PREDICT_H) $(VEC_H) $(FUNCTION_H) cfghooks.h
GIMPLE_H = gimple.h gimple.def gsstruct.def pointer-set.h $(VEC_H) \
- $(GGC_H) $(BASIC_BLOCK_H) $(TARGET_H) tree-ssa-operands.h \
- tree-ssa-alias.h vecir.h
+ vecir.h $(GGC_H) $(BASIC_BLOCK_H) $(TARGET_H) tree-ssa-operands.h \
+ tree-ssa-alias.h $(INTERNAL_FN_H)
GCOV_IO_H = gcov-io.h gcov-iov.h auto-host.h
COVERAGE_H = coverage.h $(GCOV_IO_H)
DEMANGLE_H = $(srcdir)/../include/demangle.h
init-regs.o \
input.o \
integrate.o \
+ internal-fn.o \
intl.o \
ira.o \
ira-build.o \
$(TM_H) $(TREE_H) $(DIAGNOSTIC_CORE_H) $(DIAGNOSTIC_H) $(TREE_FLOW_H) \
$(TREE_PASS_H) tree-ssa-propagate.h tree-pretty-print.h \
gimple-pretty-print.h
+internal-fn.o : internal-fn.c $(CONFIG_H) $(SYSTEM_H) coretypes.h \
+ $(INTERNAL_FN_H) $(TREE_H) $(EXPR_H) $(OPTABS_H) $(GIMPLE_H)
gimple.o : gimple.c $(CONFIG_H) $(SYSTEM_H) coretypes.h $(TREE_H) \
$(GGC_H) $(GIMPLE_H) $(DIAGNOSTIC_CORE_H) $(DIAGNOSTIC_H) gt-gimple.h \
$(TREE_FLOW_H) value-prof.h $(FLAGS_H) $(DEMANGLE_H) \
static void
expand_call_stmt (gimple stmt)
{
- tree exp, decl, lhs = gimple_call_lhs (stmt);
+ tree exp, decl, lhs;
bool builtin_p;
size_t i;
+ if (gimple_call_internal_p (stmt))
+ {
+ expand_internal_call (stmt);
+ return;
+ }
+
exp = build_vl_exp (CALL_EXPR, gimple_call_num_args (stmt) + 3);
CALL_EXPR_FN (exp) = gimple_call_fn (stmt);
SET_EXPR_LOCATION (exp, gimple_location (stmt));
TREE_BLOCK (exp) = gimple_block (stmt);
+ lhs = gimple_call_lhs (stmt);
if (lhs)
expand_assignment (lhs, exp, false);
else
if (code == SSA_NAME
&& (g = SSA_NAME_DEF_STMT (ssa_name))
&& gimple_code (g) == GIMPLE_CALL)
- pmode = promote_function_mode (type, mode, &unsignedp,
- gimple_call_fntype (g),
- 2);
+ {
+ gcc_assert (!gimple_call_internal_p (g));
+ pmode = promote_function_mode (type, mode, &unsignedp,
+ gimple_call_fntype (g),
+ 2);
+ }
else
pmode = promote_decl_mode (exp, &unsignedp);
gcc_assert (GET_MODE (decl_rtl) == pmode);
/* Check for virtual calls that became direct calls. */
callee = gimple_call_fn (stmt);
- if (TREE_CODE (callee) == OBJ_TYPE_REF)
+ if (callee && TREE_CODE (callee) == OBJ_TYPE_REF)
{
tree binfo, fndecl, delta, obj;
HOST_WIDE_INT token;
case GIMPLE_CALL:
{
- tree fn = (*valueize) (gimple_call_fn (stmt));
+ tree fn;
+
+ if (gimple_call_internal_p (stmt))
+ /* No folding yet for these functions. */
+ return NULL_TREE;
+
+ fn = (*valueize) (gimple_call_fn (stmt));
if (TREE_CODE (fn) == ADDR_EXPR
&& TREE_CODE (TREE_OPERAND (fn, 0)) == FUNCTION_DECL
&& DECL_BUILT_IN (TREE_OPERAND (fn, 0)))
tree parms, p;
unsigned int i, nargs;
+ /* Calls to internal functions always match their signature. */
+ if (gimple_call_internal_p (stmt))
+ return true;
+
nargs = gimple_call_num_args (stmt);
/* Get argument types for verification. */
if (flags & TDF_RAW)
{
- dump_gimple_fmt (buffer, spc, flags, "%G <%T, %T",
- gs, gimple_call_fn (gs), lhs);
+ if (gimple_call_internal_p (gs))
+ dump_gimple_fmt (buffer, spc, flags, "%G <%s, %T", gs,
+ internal_fn_name (gimple_call_internal_fn (gs)), lhs);
+ else
+ dump_gimple_fmt (buffer, spc, flags, "%G <%T, %T",
+ gs, gimple_call_fn (gs), lhs);
if (gimple_call_num_args (gs) > 0)
{
pp_string (buffer, ", ");
pp_space (buffer);
}
- print_call_name (buffer, gimple_call_fn (gs), flags);
+ if (gimple_call_internal_p (gs))
+ pp_string (buffer, internal_fn_name (gimple_call_internal_fn (gs)));
+ else
+ print_call_name (buffer, gimple_call_fn (gs), flags);
pp_string (buffer, " (");
dump_gimple_call_args (buffer, gs, flags);
pp_character (buffer, ')');
}
+/* 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. */
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. */
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));
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])
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])
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;
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++)
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;
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;
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));
#include "basic-block.h"
#include "tree-ssa-operands.h"
#include "tree-ssa-alias.h"
+#include "internal-fn.h"
struct gimple_seq_node_d;
typedef struct gimple_seq_node_d *gimple_seq_node;
GF_CALL_VA_ARG_PACK = 1 << 4,
GF_CALL_NOTHROW = 1 << 5,
GF_CALL_ALLOCA_FOR_VAR = 1 << 6,
+ GF_CALL_INTERNAL = 1 << 7,
GF_OMP_PARALLEL_COMBINED = 1 << 0,
/* True on an GIMPLE_OMP_RETURN statement if the return does not require
struct pt_solution call_clobbered;
/* [ WORD 13 ] */
- tree fntype;
+ union GTY ((desc ("%1.membase.opbase.gsbase.subcode & GF_CALL_INTERNAL"))) {
+ tree GTY ((tag ("0"))) fntype;
+ enum internal_fn GTY ((tag ("GF_CALL_INTERNAL"))) internal_fn;
+ } u;
/* [ WORD 14 ]
Operand vector. NOTE! This must always be the last field
gimple gimple_build_call_vec (tree, VEC(tree, heap) *);
gimple gimple_build_call (tree, unsigned, ...);
+gimple gimple_build_call_internal (enum internal_fn, unsigned, ...);
+gimple gimple_build_call_internal_vec (enum internal_fn, VEC(tree, heap) *);
gimple gimple_build_call_from_tree (tree);
gimple gimplify_assign (tree, tree, gimple_seq *);
gimple gimple_build_cond (enum tree_code, tree, tree, tree, tree);
void gimple_seq_free (gimple_seq);
void gimple_seq_add_seq (gimple_seq *, gimple_seq);
gimple_seq gimple_seq_copy (gimple_seq);
+bool gimple_call_same_target_p (const_gimple, const_gimple);
int gimple_call_flags (const_gimple);
int gimple_call_return_flags (const_gimple);
int gimple_call_arg_flags (const_gimple, unsigned);
}
+/* Return true if call GS calls an internal-only function, as enumerated
+ by internal_fn. */
+
+static inline bool
+gimple_call_internal_p (const_gimple gs)
+{
+ GIMPLE_CHECK (gs, GIMPLE_CALL);
+ return (gs->gsbase.subcode & GF_CALL_INTERNAL) != 0;
+}
+
+
+/* Return the target of internal call GS. */
+
+static inline enum internal_fn
+gimple_call_internal_fn (const_gimple gs)
+{
+ gcc_gimple_checking_assert (gimple_call_internal_p (gs));
+ return gs->gimple_call.u.internal_fn;
+}
+
+
/* Return the function type of the function called by GS. */
static inline tree
gimple_call_fntype (const_gimple gs)
{
GIMPLE_CHECK (gs, GIMPLE_CALL);
- return gs->gimple_call.fntype;
+ if (gimple_call_internal_p (gs))
+ return NULL_TREE;
+ return gs->gimple_call.u.fntype;
}
/* Set the type of the function called by GS to FNTYPE. */
gimple_call_set_fntype (gimple gs, tree fntype)
{
GIMPLE_CHECK (gs, GIMPLE_CALL);
- gs->gimple_call.fntype = fntype;
+ gcc_gimple_checking_assert (!gimple_call_internal_p (gs));
+ gs->gimple_call.u.fntype = fntype;
}
gimple_call_set_fn (gimple gs, tree fn)
{
GIMPLE_CHECK (gs, GIMPLE_CALL);
+ gcc_gimple_checking_assert (!gimple_call_internal_p (gs));
gimple_set_op (gs, 1, fn);
}
gimple_call_set_fndecl (gimple gs, tree decl)
{
GIMPLE_CHECK (gs, GIMPLE_CALL);
+ gcc_gimple_checking_assert (!gimple_call_internal_p (gs));
gimple_set_op (gs, 1, build_fold_addr_expr_loc (gimple_location (gs), decl));
}
+
+/* Set internal function FN to be the function called by call statement GS. */
+
+static inline void
+gimple_call_set_internal_fn (gimple gs, enum internal_fn fn)
+{
+ GIMPLE_CHECK (gs, GIMPLE_CALL);
+ gcc_gimple_checking_assert (gimple_call_internal_p (gs));
+ gs->gimple_call.u.internal_fn = fn;
+}
+
+
/* Given a valid GIMPLE_CALL function address return the FUNCTION_DECL
associated with the callee if known. Otherwise return NULL_TREE. */
static inline tree
gimple_call_addr_fndecl (const_tree fn)
{
- if (TREE_CODE (fn) == ADDR_EXPR)
+ if (fn && TREE_CODE (fn) == ADDR_EXPR)
{
tree fndecl = TREE_OPERAND (fn, 0);
if (TREE_CODE (fndecl) == MEM_REF
{
tree type = gimple_call_fntype (gs);
+ if (type == NULL_TREE)
+ return TREE_TYPE (gimple_call_lhs (gs));
+
/* The type returned by a function is the type of its
function type. */
return TREE_TYPE (type);
--- /dev/null
+/* Internal functions.
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#include "config.h"
+#include "system.h"
+#include "coretypes.h"
+#include "internal-fn.h"
+#include "tree.h"
+#include "expr.h"
+#include "optabs.h"
+#include "gimple.h"
+
+/* The names of each internal function, indexed by function number. */
+const char *const internal_fn_name_array[] = {
+#define DEF_INTERNAL_FN(CODE, FLAGS) #CODE,
+#include "internal-fn.def"
+#undef DEF_INTERNAL_FN
+ "<invalid-fn>"
+};
+
+/* The ECF_* flags of each internal function, indexed by function number. */
+const int internal_fn_flags_array[] = {
+#define DEF_INTERNAL_FN(CODE, FLAGS) FLAGS,
+#include "internal-fn.def"
+#undef DEF_INTERNAL_FN
+ 0
+};
+
+/* Routines to expand each internal function, indexed by function number.
+ Each routine has the prototype:
+
+ expand_<NAME> (gimple stmt)
+
+ where STMT is the statement that performs the call. */
+static void (*const internal_fn_expanders[]) (gimple) = {
+#define DEF_INTERNAL_FN(CODE, FLAGS) expand_##CODE,
+#include "internal-fn.def"
+#undef DEF_INTERNAL_FN
+ 0
+};
+
+/* Expand STMT, which is a call to internal function FN. */
+
+void
+expand_internal_call (gimple stmt)
+{
+ internal_fn_expanders[(int) gimple_call_internal_fn (stmt)] (stmt);
+}
--- /dev/null
+/* Internal functions.
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+/* This file specifies a list of internal "functions". These functions
+ differ from built-in functions in that they have no linkage and cannot
+ be called directly by the user. They represent operations that are only
+ synthesised by GCC itself.
+
+ Internal functions are used instead of tree codes if the operation
+ and its operands are more naturally represented as a GIMPLE_CALL
+ than a GIMPLE_ASSIGN.
+
+ Each entry in this file has the form:
+
+ DEF_INTERNAL_FN (NAME, FLAGS)
+
+ where NAME is the name of the function and FLAGS is a set of
+ ECF_* flags. Each entry must have a corresponding expander
+ of the form:
+
+ void expand_NAME (gimple stmt)
+
+ where STMT is the statement that performs the call. */
--- /dev/null
+/* Internal functions.
+ Copyright (C) 2011 Free Software Foundation, Inc.
+
+This file is part of GCC.
+
+GCC is free software; you can redistribute it and/or modify it under
+the terms of the GNU General Public License as published by the Free
+Software Foundation; either version 3, or (at your option) any later
+version.
+
+GCC is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or
+FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+for more details.
+
+You should have received a copy of the GNU General Public License
+along with GCC; see the file COPYING3. If not see
+<http://www.gnu.org/licenses/>. */
+
+#ifndef GCC_INTERNAL_FN_H
+#define GCC_INTERNAL_FN_H
+
+enum internal_fn {
+#define DEF_INTERNAL_FN(CODE, FLAGS) IFN_##CODE,
+#include "internal-fn.def"
+#undef DEF_INTERNAL_FN
+ IFN_LAST
+};
+
+/* Return the name of internal function FN. The name is only meaningful
+ for dumps; it has no linkage. */
+
+static inline const char *
+internal_fn_name (enum internal_fn fn)
+{
+ extern const char *const internal_fn_name_array[];
+ return internal_fn_name_array[(int) fn];
+}
+
+/* Return the ECF_* flags for function FN. */
+
+static inline int
+internal_fn_flags (enum internal_fn fn)
+{
+ extern const int internal_fn_flags_array[];
+ return internal_fn_flags_array[(int) fn];
+}
+
+extern void expand_internal_call (gimple);
+
+#endif
{
tree target = gimple_call_fn (call);
+ if (!target)
+ return;
if (TREE_CODE (target) == SSA_NAME)
ipa_analyze_indirect_call_uses (node, info, parms_info, call, target);
else if (TREE_CODE (target) == OBJ_TYPE_REF)
}
}
if (is_gimple_call (stmt))
- gimple_call_set_fntype (stmt, lto_input_tree (ib, data_in));
+ {
+ if (gimple_call_internal_p (stmt))
+ gimple_call_set_internal_fn
+ (stmt, (enum internal_fn) lto_input_sleb128 (ib));
+ else
+ gimple_call_set_fntype (stmt, lto_input_tree (ib, data_in));
+ }
break;
case GIMPLE_NOP:
lto_output_tree_ref (ob, op);
}
if (is_gimple_call (stmt))
- lto_output_tree_ref (ob, gimple_call_fntype (stmt));
+ {
+ if (gimple_call_internal_p (stmt))
+ output_sleb128 (ob, (int) gimple_call_internal_fn (stmt));
+ else
+ lto_output_tree_ref (ob, gimple_call_fntype (stmt));
+ }
break;
case GIMPLE_NOP:
tree fntype, fndecl;
unsigned i;
- if (!is_gimple_call_addr (fn))
+ if (gimple_call_internal_p (stmt))
+ {
+ if (fn)
+ {
+ error ("gimple call has two targets");
+ debug_generic_stmt (fn);
+ return true;
+ }
+ }
+ else
+ {
+ if (!fn)
+ {
+ error ("gimple call has no target");
+ return true;
+ }
+ }
+
+ if (fn && !is_gimple_call_addr (fn))
{
error ("invalid function in gimple call");
debug_generic_stmt (fn);
return true;
}
- if (!POINTER_TYPE_P (TREE_TYPE (fn))
- || (TREE_CODE (TREE_TYPE (TREE_TYPE (fn))) != FUNCTION_TYPE
- && TREE_CODE (TREE_TYPE (TREE_TYPE (fn))) != METHOD_TYPE))
+ if (fn
+ && (!POINTER_TYPE_P (TREE_TYPE (fn))
+ || (TREE_CODE (TREE_TYPE (TREE_TYPE (fn))) != FUNCTION_TYPE
+ && TREE_CODE (TREE_TYPE (TREE_TYPE (fn))) != METHOD_TYPE)))
{
error ("non-function in gimple call");
return true;
}
fntype = gimple_call_fntype (stmt);
- if (gimple_call_lhs (stmt)
+ if (fntype
+ && gimple_call_lhs (stmt)
&& !useless_type_conversion_p (TREE_TYPE (gimple_call_lhs (stmt)),
TREE_TYPE (fntype))
/* ??? At least C++ misses conversions at assignments from
case GIMPLE_CALL:
if (gimple_call_lhs (g))
break;
+ if (gimple_call_internal_p (g))
+ break;
/* This is a naked call, as opposed to a GIMPLE_CALL with an
LHS. All calls whose value is ignored should be
|| gimple_call_lhs (twos)
|| gimple_call_chain (ones)
|| gimple_call_chain (twos)
- || !operand_equal_p (gimple_call_fn (ones), gimple_call_fn (twos), 0)
+ || !gimple_call_same_target_p (ones, twos)
|| gimple_call_num_args (ones) != gimple_call_num_args (twos))
return false;
return true;
}
+ /* Internal calls provide no argument types, so the extra laxity
+ for normal calls does not apply. */
+ if (gimple_call_internal_p (stmt))
+ return false;
+
/* Propagate into the call arguments. Compared to replace_uses_in
this can use the argument slot types for type verification
instead of the current argument type. We also can safely
struct { enum tree_code op; tree opnd; } unary;
struct { enum tree_code op; tree opnd0, opnd1; } binary;
struct { enum tree_code op; tree opnd0, opnd1, opnd2; } ternary;
- struct { tree fn; bool pure; size_t nargs; tree *args; } call;
+ struct { gimple fn_from; bool pure; size_t nargs; tree *args; } call;
} ops;
};
expr->type = TREE_TYPE (gimple_call_lhs (stmt));
expr->kind = EXPR_CALL;
- expr->ops.call.fn = gimple_call_fn (stmt);
+ expr->ops.call.fn_from = stmt;
if (gimple_call_flags (stmt) & (ECF_CONST | ECF_PURE))
expr->ops.call.pure = true;
/* If the calls are to different functions, then they
clearly cannot be equal. */
- if (! operand_equal_p (expr0->ops.call.fn,
- expr1->ops.call.fn, 0))
+ if (!gimple_call_same_target_p (expr0->ops.call.fn_from,
+ expr1->ops.call.fn_from))
return false;
if (! expr0->ops.call.pure)
{
size_t i;
enum tree_code code = CALL_EXPR;
+ gimple fn_from;
val = iterative_hash_object (code, val);
- val = iterative_hash_expr (expr->ops.call.fn, val);
+ fn_from = expr->ops.call.fn_from;
+ if (gimple_call_internal_p (fn_from))
+ val = iterative_hash_hashval_t
+ ((hashval_t) gimple_call_internal_fn (fn_from), val);
+ else
+ val = iterative_hash_expr (gimple_call_fn (fn_from), val);
for (i = 0; i < expr->ops.call.nargs; i++)
val = iterative_hash_expr (expr->ops.call.args[i], val);
}
{
size_t i;
size_t nargs = element->expr.ops.call.nargs;
-
- print_generic_expr (stream, element->expr.ops.call.fn, 0);
+ gimple fn_from;
+
+ fn_from = element->expr.ops.call.fn_from;
+ if (gimple_call_internal_p (fn_from))
+ fputs (internal_fn_name (gimple_call_internal_fn (fn_from)),
+ stream);
+ else
+ print_generic_expr (stream, gimple_call_fn (fn_from), 0);
fprintf (stream, " (");
for (i = 0; i < nargs; i++)
{
}
/* Return true if we can value number the call in STMT. This is true
- if we have a pure or constant call. */
+ if we have a pure or constant call to a real function. */
static bool
can_value_number_call (gimple stmt)
{
+ if (gimple_call_internal_p (stmt))
+ return false;
if (gimple_call_flags (stmt) & (ECF_PURE | ECF_CONST))
return true;
return false;
{
tree orig_fn = gimple_call_fn (stmt);
tree fn;
+ if (!orig_fn)
+ continue;
if (TREE_CODE (orig_fn) == SSA_NAME)
fn = VN_INFO (orig_fn)->valnum;
else if (TREE_CODE (orig_fn) == OBJ_TYPE_REF
/* ??? We should handle stores from calls. */
else if (TREE_CODE (lhs) == SSA_NAME)
{
- if (gimple_call_flags (stmt) & (ECF_PURE | ECF_CONST))
+ if (!gimple_call_internal_p (stmt)
+ && gimple_call_flags (stmt) & (ECF_PURE | ECF_CONST))
changed = visit_reference_op_call (lhs, stmt);
else
changed = defs_to_varying (stmt);
{
tree decl;
+ gcc_assert (!gimple_call_internal_p (call));
+
/* If we can directly resolve the function being called, do so.
Otherwise, it must be some sort of indirect expression that
we should still be able to handle. */
/* Fallthru to general call handling. */;
}
if (!in_ipa_mode
+ || gimple_call_internal_p (t)
|| (fndecl
&& (!(fi = lookup_vi_for_tree (fndecl))
|| !fi->is_fn_info)))
if (gimple_call_fndecl (stmt) != NULL_TREE)
return false;
+ if (gimple_call_internal_p (stmt))
+ return false;
+
histogram = gimple_histogram_value_of_type (cfun, stmt, HIST_TYPE_INDIR_CALL);
if (!histogram)
return false;
tree callee;
if (gimple_code (stmt) != GIMPLE_CALL
+ || gimple_call_internal_p (stmt)
|| gimple_call_fndecl (stmt) != NULL_TREE)
return;