diff mbox

RFA: Gimple calls to "internal" functions

Message ID BANLkTikv==MM0AWAinFdPeJO+82JhEY2fw@mail.gmail.com
State New
Headers show

Commit Message

Richard Biener April 18, 2011, 11:53 a.m. UTC
On Mon, Apr 18, 2011 at 11:26 AM, Richard Sandiford
<richard.sandiford@linaro.org> wrote:
> Diego Novillo <dnovillo@google.com> writes:
>> On Thu, Apr 14, 2011 at 09:43, Richard Sandiford
>> <richard.sandiford@linaro.org> wrote:
>>> +/* 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.  */
>>
>> Could you add a short description specifying how these internal
>> functions are later expanded into RTL?
>
> Sure, added.
>
>> So, this patch adds no internal functions at all?
>
> Yeah.  The first ones are defined by:
>
>    http://gcc.gnu.org/ml/gcc-patches/2011-04/msg00881.html
>
>> We need to make sure that internal functions are properly streamed
>> in/out for LTO.  So, we should have some test cases for it.
>
> Gah, I'd even thought about streaming for the first (subcode-based)
> implementation.  Streaming just worked then though.  It just doesn't
> work now.
>
> As far as tests go, I'd like to run the main vectoriser tests with
> -flto, which would trap all uses of the first batch of functions.
>
> How does this updated patch look?  Bootstrapped & regression-tested
> on x86_64-linux-gnu, and tested on arm-linux-gnueabi.
>
> Richard
>
>
> gcc/
>        * 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): Assert that the call is not internal.
>        (gimple_call_fn): Assert that the function is nonnull.
>        (gimple_call_set_internal_fn): New function.
>        (gimple_call_set_fn): Clear GF_CALL_INTERNAL field.
>        (gimple_call_fndecl): Return null for calls to internal functions.
>        (gimple_call_return_type): Use the type of the lhs for internal calls.
>        * gimple.c (gimple_build_call_1): Use gimple_call_set_fn rather
>        than gimple_set_op.
>        (gimple_build_call_internal_1): New function.
>        (gimple_build_call_internal): Likewise.
>        (gimple_build_call_internal_vec): Likewise.
>        (walk_gimple_op): Skip the GIMPLE_CALL function for calls to
>        internal functions.
>        (gimple_call_same_target_p): New function.
>        (gimple_call_flags): Handle calls to internal functions.
>        (gimple_call_arg_flags): Likewise.
>        (gimple_call_return_flags): Likewise.
>        (gimple_has_side_effects): Likewise.
>        (gimple_call_copy_skip_args): Likewise.
>        * cfgexpand.c (expand_gimple_call_internal): New function.
>        (expand_call_stmt): Use it.
>        * expr.c (expand_expr_real_1): Don't call promote_function_mode
>        for internal functions.
>        * gimple-fold.c (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): Likewise.
>        * tree-cfg.c (verify_gimple_call): Likewise.
>        (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 (eliminate): Likewise.
>        * tree-ssa-sccvn.c (copy_reference_ops_from_call): Likewise.
>        * 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.
>
> Index: gcc/Makefile.in
> ===================================================================
> --- gcc/Makefile.in     2011-04-18 10:18:49.000000000 +0100
> +++ gcc/Makefile.in     2011-04-18 10:18:54.000000000 +0100
> @@ -893,6 +893,8 @@ RTL_ERROR_H = $(RTL_H) $(DIAGNOSTIC_CORE
>  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) \
> @@ -901,8 +903,8 @@ TREE_H = tree.h all-tree.def tree.def c-
>  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
> @@ -1274,6 +1276,7 @@ OBJS-common = \
>        init-regs.o \
>        input.o \
>        integrate.o \
> +       internal-fn.o \
>        intl.o \
>        ira.o \
>        ira-build.o \
> @@ -2759,6 +2762,8 @@ tree-object-size.o: tree-object-size.c $
>    $(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.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) \
> Index: gcc/internal-fn.def
> ===================================================================
> --- /dev/null   2011-03-23 08:42:11.268792848 +0000
> +++ gcc/internal-fn.def 2011-04-18 10:20:35.000000000 +0100
> @@ -0,0 +1,39 @@
> +/* 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 (tree lhs, tree *args)
> +
> +   where LHS and ARGS are as for expand_internal_call.  */
> Index: gcc/internal-fn.h
> ===================================================================
> --- /dev/null   2011-03-23 08:42:11.268792848 +0000
> +++ gcc/internal-fn.h   2011-04-18 10:18:54.000000000 +0100
> @@ -0,0 +1,51 @@
> +/* 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 (enum internal_fn, tree, tree *);
> +
> +#endif
> Index: gcc/internal-fn.c
> ===================================================================
> --- /dev/null   2011-03-23 08:42:11.268792848 +0000
> +++ gcc/internal-fn.c   2011-04-18 10:18:54.000000000 +0100
> @@ -0,0 +1,64 @@
> +/* 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"
> +
> +/* 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> (tree lhs, tree *args)
> +
> +   where LHS and ARGS are as for expand_internal_call.  */
> +static void (*const internal_fn_expanders[]) (tree, tree *) = {
> +#define DEF_INTERNAL_FN(CODE, FLAGS) expand_##CODE,
> +#include "internal-fn.def"
> +#undef DEF_INTERNAL_FN
> +  0
> +};
> +
> +/* Expand a call to internal function FN.  ARGS is an array of the
> +   function's arguments and LHS is where the result should be stored.  */
> +
> +void
> +expand_internal_call (enum internal_fn fn, tree lhs, tree *args)
> +{
> +  internal_fn_expanders[(int) fn] (lhs, args);

Can you use an interface that simply takes a gimple statement?
We want to eventually transistion to expanding directly from them
without re-creating a GENERIC call-expr for 4.7.

> +}
> Index: gcc/gimple.h
> ===================================================================
> --- gcc/gimple.h        2011-04-18 10:18:49.000000000 +0100
> +++ gcc/gimple.h        2011-04-18 10:18:54.000000000 +0100
> @@ -30,6 +30,7 @@ #define GCC_GIMPLE_H
>  #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;
> @@ -102,6 +103,7 @@ enum gf_mask {
>     GF_CALL_TAILCALL           = 1 << 3,
>     GF_CALL_VA_ARG_PACK                = 1 << 4,
>     GF_CALL_NOTHROW            = 1 << 5,
> +    GF_CALL_INTERNAL           = 1 << 6,
>     GF_OMP_PARALLEL_COMBINED   = 1 << 0,
>
>     /* True on an GIMPLE_OMP_RETURN statement if the return does not require
> @@ -406,7 +408,10 @@ struct GTY(()) gimple_statement_call
>   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
> @@ -820,6 +825,8 @@ #define gimple_build_debug_bind(var,val,
>
>  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);
> @@ -864,6 +871,7 @@ gimple_seq gimple_seq_alloc (void);
>  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);
> @@ -2004,13 +2012,35 @@ gimple_call_set_lhs (gimple gs, tree lhs
>  }
>
>
> +/* 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;
> +  gcc_gimple_checking_assert (!gimple_call_internal_p (gs));

I'm not sure this is the best fallback but it's certainly safe ;)
I think we should return NULL for internal fns, that is sure to trap
also for non-checking-enabled builds.

> +  return gs->gimple_call.u.fntype;
>  }
>
>  /* Set the type of the function called by GS to FNTYPE.  */
> @@ -2019,7 +2049,8 @@ gimple_call_fntype (const_gimple gs)
>  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;
>  }
>
>
> @@ -2029,8 +2060,12 @@ gimple_call_set_fntype (gimple gs, tree
>  static inline tree
>  gimple_call_fn (const_gimple gs)
>  {
> +  tree op;
> +
>   GIMPLE_CHECK (gs, GIMPLE_CALL);
> -  return gimple_op (gs, 1);
> +  op = gimple_op (gs, 1);
> +  gcc_gimple_checking_assert (op != NULL_TREE);

Hmmm... probably good for your time of development but please remove
this.  Rather add a hunk to verify_gimple_call that checks that internal
fns have a NULL fn.

> +  return op;
>  }
>
>  /* Return a pointer to the tree node representing the function called by call
> @@ -2044,12 +2079,25 @@ gimple_call_fn_ptr (const_gimple gs)
>  }
>
>
> +/* 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);
> +  gs->gsbase.subcode |= GF_CALL_INTERNAL;
> +  gimple_set_op (gs, 1, NULL_TREE);

can we instead assert this?  We shouldn't ever change a calls state
from non-internal to internal (or vice-versa).

> +  gs->gimple_call.u.internal_fn = fn;
> +}
> +
> +
>  /* Set FN to be the function called by call statement GS.  */
>
>  static inline void
>  gimple_call_set_fn (gimple gs, tree fn)
>  {
>   GIMPLE_CHECK (gs, GIMPLE_CALL);
> +  gs->gsbase.subcode &= ~GF_CALL_INTERNAL;

Likewise.  Or just let the stmt verifier catch it.

>   gimple_set_op (gs, 1, fn);
>  }
>
> @@ -2071,7 +2119,12 @@ gimple_call_set_fndecl (gimple gs, tree
>  static inline tree
>  gimple_call_fndecl (const_gimple gs)
>  {
> -  tree addr = gimple_call_fn (gs);
> +  tree addr;
> +
> +  if (gimple_call_internal_p (gs))
> +    return NULL_TREE;

No need for this if you remove the assert from gimple_call_fn and
simply return NULL there ...

> +  addr = gimple_call_fn (gs);
>   if (TREE_CODE (addr) == ADDR_EXPR)
>     {
>       tree fndecl = TREE_OPERAND (addr, 0);
> @@ -2094,7 +2147,12 @@ gimple_call_fndecl (const_gimple gs)
>  static inline tree
>  gimple_call_return_type (const_gimple gs)
>  {
> -  tree type = gimple_call_fntype (gs);
> +  tree type;
> +
> +  if (gimple_call_internal_p (gs))
> +    return TREE_TYPE (gimple_call_lhs (gs));
> +
> +  type = gimple_call_fntype (gs);
>
>   /* The type returned by a function is the type of its
>      function type.  */
> Index: gcc/gimple.c
> ===================================================================
> --- gcc/gimple.c        2011-04-18 10:18:49.000000000 +0100
> +++ gcc/gimple.c        2011-04-18 10:18:54.000000000 +0100
> @@ -230,7 +230,7 @@ gimple_build_call_1 (tree fn, unsigned n
>   gimple s = gimple_build_with_ops (GIMPLE_CALL, ERROR_MARK, nargs + 3);
>   if (TREE_CODE (fn) == FUNCTION_DECL)
>     fn = build_fold_addr_expr (fn);
> -  gimple_set_op (s, 1, fn);
> +  gimple_call_set_fn (s, fn);
>   gimple_call_set_fntype (s, TREE_TYPE (TREE_TYPE (fn)));
>   gimple_call_reset_alias_info (s);
>   return s;
> @@ -277,6 +277,58 @@ gimple_build_call (tree fn, unsigned nar
>  }
>
>
> +/* 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);
> +  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.  */
> @@ -1399,9 +1451,12 @@ walk_gimple_op (gimple stmt, walk_tree_f
>       if (ret)
>         return ret;
>
> -      ret = walk_tree (gimple_call_fn_ptr (stmt), callback_op, wi, pset);
> -      if (ret)
> -        return ret;
> +      if (!gimple_call_internal_p (stmt))
> +       {
> +         ret = walk_tree (gimple_call_fn_ptr (stmt), callback_op, wi, pset);
> +         if (ret)
> +           return ret;
> +       }
>
>       for (i = 0; i < gimple_call_num_args (stmt); i++)
>        {
> @@ -1773,6 +1828,19 @@ 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_internal_p (c2)
> +           && operand_equal_p (gimple_call_fn (c1), gimple_call_fn (c2), 0));

After a patch I am about to commit in a sec. the following for comparing
gimple_call_fn would be better:

  gimple_call_fn (c1) == gimple_call_fn (c2)
  || (gimple_call_fndecl (c1)
     && gimple_call_fndecl (c1) == gimple_call_fndecl (c2))

because we have several forms of specifying a function-decl.

> +}
> +
>  /* Detect flags from a GIMPLE_CALL.  This is just like
>    call_expr_flags, but for gimple tuples.  */
>
> @@ -1784,6 +1852,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,8 +1868,14 @@ gimple_call_flags (const_gimple 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));
> +  tree type;
> +  tree attr;
> +
> +  if (gimple_call_internal_p (stmt))
> +    return 0;
> +
> +  type = gimple_call_fntype (stmt);
> +  attr = lookup_attribute ("fn spec", TYPE_ATTRIBUTES (type));
>   if (!attr)
>     return 0;
>
> @@ -1839,6 +1915,9 @@ gimple_call_return_flags (const_gimple s
>   tree type;
>   tree attr = NULL_TREE;
>
> +  if (gimple_call_internal_p (stmt))
> +    return 0;
> +
>   if (gimple_call_flags (stmt) & ECF_MALLOC)
>     return ERF_NOALIAS;
>
> @@ -2287,7 +2366,8 @@ gimple_has_side_effects (const_gimple s)
>          return true;
>        }
>
> -      if (TREE_SIDE_EFFECTS (gimple_call_fn (s)))
> +      if (!gimple_call_internal_p (s)
> +         && TREE_SIDE_EFFECTS (gimple_call_fn (s)))
>         return true;
>
>       for (i = 0; i < nargs; i++)
> @@ -2332,8 +2412,9 @@ gimple_rhs_has_side_effects (const_gimpl
>
>       /* 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)))
> +      if (!gimple_call_internal_p (s)
> +         && (TREE_SIDE_EFFECTS (gimple_call_fn (s))
> +             || TREE_THIS_VOLATILE (gimple_call_fn (s))))
>        {
>          gcc_assert (gimple_has_volatile_ops (s));
>          return true;
> @@ -3089,7 +3170,6 @@ canonicalize_cond_expr_cond (tree t)
>  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;
> @@ -3098,7 +3178,11 @@ gimple_call_copy_skip_args (gimple 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);

This code looks like it could be simplified by using gimple_copy, a
loop to set arguments and gimple_set_num_ops.  That of course looks
unrelated to your change, but it makes apparent that sth is wrong with
this function ;)

>   VEC_free (tree, heap, vargs);
>   if (gimple_call_lhs (stmt))
>     gimple_call_set_lhs (new_stmt, gimple_call_lhs (stmt));
> Index: gcc/cfgexpand.c
> ===================================================================
> --- gcc/cfgexpand.c     2011-04-18 10:18:49.000000000 +0100
> +++ gcc/cfgexpand.c     2011-04-18 10:18:54.000000000 +0100
> @@ -1831,16 +1831,39 @@ expand_gimple_cond (basic_block bb, gimp
>   return new_bb;
>  }
>
> +/* A subroutine of expand_call_stmt.  Expand GIMPLE_CALL statement STMT,
> +   which is known to be to an internal function.  */
> +
> +static void
> +expand_gimple_call_internal (gimple stmt)
> +{
> +  tree lhs;
> +  tree *args;
> +  size_t i;
> +
> +  lhs = gimple_call_lhs (stmt);
> +  args = XALLOCAVEC (tree, gimple_call_num_args (stmt));
> +  for (i = 0; i < gimple_call_num_args (stmt); i++)
> +    args[i] = gimple_call_arg (stmt, i);
> +  expand_internal_call (gimple_call_internal_fn (stmt), lhs, args);

So, expand_internal_call should simply take the stmt.  Micha has at
least (old) patches to also make builtins expand directly from a gimple
stmt.

> +}
> +
>  /* A subroutine of expand_gimple_stmt_1, expanding one GIMPLE_CALL
>    statement STMT.  */
>
>  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_gimple_call_internal (stmt);
> +      return;
> +    }
> +
>   exp = build_vl_exp (CALL_EXPR, gimple_call_num_args (stmt) + 3);
>
>   CALL_EXPR_FN (exp) = gimple_call_fn (stmt);
> @@ -1885,6 +1908,7 @@ expand_call_stmt (gimple 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
> Index: gcc/expr.c
> ===================================================================
> --- gcc/expr.c  2011-04-18 10:18:49.000000000 +0100
> +++ gcc/expr.c  2011-04-18 10:18:54.000000000 +0100
> @@ -8521,10 +8521,15 @@ expand_expr_real_1 (tree exp, rtx target
>          enum machine_mode pmode;
>
>          /* Get the signedness to be used for this variable.  Ensure we get
> -            the same mode we got when the variable was declared.  */
> +            the same mode we got when the variable was declared.
> +
> +            Note that calls to internal functions do not result in a
> +            call instruction, so promote_function_mode is not meaningful
> +            in that case.  */
>          if (code == SSA_NAME
>              && (g = SSA_NAME_DEF_STMT (ssa_name))
> -             && gimple_code (g) == GIMPLE_CALL)
> +             && gimple_code (g) == GIMPLE_CALL
> +             && !gimple_call_internal_p (g))

I'm not sure we'll never need to do promotions for internal functions
but at least for now this looks ok, and we can think of how to deal
with this when it's necessary.  Of course it looks fishy that you
even arrive here ...?

>            pmode = promote_function_mode (type, mode, &unsignedp,
>                                           gimple_call_fntype (g),
>                                           2);
> Index: gcc/gimple-fold.c
> ===================================================================
> --- gcc/gimple-fold.c   2011-04-18 10:18:49.000000000 +0100
> +++ gcc/gimple-fold.c   2011-04-18 10:18:54.000000000 +0100
> @@ -2856,7 +2856,13 @@ gimple_fold_stmt_to_constant_1 (gimple s
>
>     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)))
> Index: gcc/gimple-low.c
> ===================================================================
> --- gcc/gimple-low.c    2011-04-18 10:18:49.000000000 +0100
> +++ gcc/gimple-low.c    2011-04-18 10:18:54.000000000 +0100
> @@ -224,6 +224,8 @@ gimple_check_call_args (gimple stmt, tre
>   /* Get argument types for verification.  */
>   if (fndecl)
>     parms = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
> +  else if (gimple_call_internal_p (stmt))
> +    parms = NULL_TREE;

Instead do a

  /* Calls to internal functions always match their signature.  */
  if (gimple_call_internal_p (stmt))
    return true;

early.

>   else
>     parms = TYPE_ARG_TYPES (gimple_call_fntype (stmt));
>
> Index: gcc/gimple-pretty-print.c
> ===================================================================
> --- gcc/gimple-pretty-print.c   2011-04-18 10:18:49.000000000 +0100
> +++ gcc/gimple-pretty-print.c   2011-04-18 10:18:54.000000000 +0100
> @@ -616,8 +616,12 @@ dump_gimple_call (pretty_printer *buffer
>
>   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, ", ");
> @@ -637,7 +641,10 @@ dump_gimple_call (pretty_printer *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, ')');
> Index: gcc/ipa-prop.c
> ===================================================================
> --- gcc/ipa-prop.c      2011-04-18 10:18:49.000000000 +0100
> +++ gcc/ipa-prop.c      2011-04-18 10:18:54.000000000 +0100
> @@ -1416,8 +1416,12 @@ ipa_analyze_call_uses (struct cgraph_nod
>                       struct ipa_node_params *info,
>                       struct param_analysis_info *parms_info, gimple call)
>  {
> -  tree target = gimple_call_fn (call);
> +  tree target;
> +
> +  if (gimple_call_internal_p (call))
> +    return;
>
> +  target = gimple_call_fn (call);
>   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)
> Index: gcc/tree-cfg.c
> ===================================================================
> --- gcc/tree-cfg.c      2011-04-18 10:18:49.000000000 +0100
> +++ gcc/tree-cfg.c      2011-04-18 10:18:54.000000000 +0100
> @@ -3042,23 +3042,43 @@ valid_fixed_convert_types_p (tree type1,
>  static bool
>  verify_gimple_call (gimple stmt)
>  {
> -  tree fn = gimple_call_fn (stmt);
> +  tree fn;
>   tree fntype, fndecl;
>   unsigned i;
>
> -  if (!is_gimple_call_addr (fn))
> +  if (!gimple_call_internal_p (stmt))

Please do not re-indent most of the function but instead do an upfront
conditionl verification for internal calls:

  if (gimple_call_internal_p (stmt))
    {
      if (gimple_op (stmt, 1) != NULL_TREE)
        {
          error ("non-NULL function in gimple internal call");
          return true;
        }
      return false;
    }

>     {
> -      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))
> -    {
> -      error ("non-function in gimple call");
> -      return true;
> +      fn = gimple_call_fn (stmt);
> +      if (!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))
> +       {
> +         error ("non-function in gimple call");
> +         return true;
> +       }
> +      fntype = gimple_call_fntype (stmt);
> +      if (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
> +            void * call results.
> +            ???  Java is completely off.  Especially with functions
> +            returning java.lang.Object.
> +            For now simply allow arbitrary pointer type conversions.  */
> +         && !(POINTER_TYPE_P (TREE_TYPE (gimple_call_lhs (stmt)))
> +              && POINTER_TYPE_P (TREE_TYPE (fntype))))
> +       {
> +         error ("invalid conversion in gimple call");
> +         debug_generic_stmt (TREE_TYPE (gimple_call_lhs (stmt)));
> +         debug_generic_stmt (TREE_TYPE (fntype));
> +         return true;
> +       }
>     }
>
>    fndecl = gimple_call_fndecl (stmt);
> @@ -3086,24 +3106,6 @@ verify_gimple_call (gimple stmt)
>       return true;
>     }
>
> -  fntype = gimple_call_fntype (stmt);
> -  if (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
> -        void * call results.
> -        ???  Java is completely off.  Especially with functions
> -        returning java.lang.Object.
> -        For now simply allow arbitrary pointer type conversions.  */
> -      && !(POINTER_TYPE_P (TREE_TYPE (gimple_call_lhs (stmt)))
> -          && POINTER_TYPE_P (TREE_TYPE (fntype))))
> -    {
> -      error ("invalid conversion in gimple call");
> -      debug_generic_stmt (TREE_TYPE (gimple_call_lhs (stmt)));
> -      debug_generic_stmt (TREE_TYPE (fntype));
> -      return true;
> -    }
> -
>   if (gimple_call_chain (stmt)
>       && !is_gimple_val (gimple_call_chain (stmt)))
>     {
> @@ -3121,7 +3123,7 @@ verify_gimple_call (gimple stmt)
>          error ("static chain in indirect gimple call");
>          return true;
>        }
> -      fn = TREE_OPERAND (fn, 0);
> +      fn = TREE_OPERAND (gimple_call_fn (stmt), 0);
>
>       if (!DECL_STATIC_CHAIN (fn))
>        {
> @@ -7436,6 +7438,8 @@ do_warn_unused_result (gimple_seq seq)
>        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
> Index: gcc/tree-eh.c
> ===================================================================
> --- gcc/tree-eh.c       2011-04-18 10:18:49.000000000 +0100
> +++ gcc/tree-eh.c       2011-04-18 10:18:54.000000000 +0100
> @@ -2743,7 +2743,7 @@ same_handler_p (gimple_seq oneh, gimple_
>       || 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)

Why do we even end up here?  internal function calls should never throw,
maybe that is what you need to adjust?

>       || gimple_call_num_args (ones) != gimple_call_num_args (twos))
>     return false;
>
> Index: gcc/tree-ssa-ccp.c
> ===================================================================
> --- gcc/tree-ssa-ccp.c  2011-04-18 10:18:49.000000000 +0100
> +++ gcc/tree-ssa-ccp.c  2011-04-18 10:18:54.000000000 +0100
> @@ -1723,6 +1723,11 @@ ccp_fold_stmt (gimple_stmt_iterator *gsi
>            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
> Index: gcc/tree-ssa-dom.c
> ===================================================================
> --- gcc/tree-ssa-dom.c  2011-04-18 10:18:49.000000000 +0100
> +++ gcc/tree-ssa-dom.c  2011-04-18 10:18:54.000000000 +0100
> @@ -64,7 +64,7 @@ struct hashable_expr
>     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;
>  };
>
> @@ -258,7 +258,7 @@ initialize_hash_element (gimple stmt, tr
>
>       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;
> @@ -422,8 +422,8 @@ hashable_expr_equal_p (const struct hash
>
>         /* 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)
> @@ -503,9 +503,15 @@ iterative_hash_hashable_expr (const stru
>       {
>         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);
>       }
> @@ -565,8 +571,14 @@ print_expr_hash_elt (FILE * stream, cons
>         {
>           size_t i;
>           size_t nargs = element->expr.ops.call.nargs;
> +          gimple fn_from;
>
> -          print_generic_expr (stream, element->expr.ops.call.fn, 0);
> +          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);

2nd time, can you add a print_gimple_call_target () function instead?

>           fprintf (stream, " (");
>           for (i = 0; i < nargs; i++)
>             {
> Index: gcc/tree-ssa-pre.c
> ===================================================================
> --- gcc/tree-ssa-pre.c  2011-04-18 10:18:49.000000000 +0100
> +++ gcc/tree-ssa-pre.c  2011-04-18 10:18:54.000000000 +0100
> @@ -4381,6 +4381,7 @@ eliminate (void)
>          /* Visit indirect calls and turn them into direct calls if
>             possible.  */
>          if (is_gimple_call (stmt)
> +             && !gimple_call_internal_p (stmt)
>              && TREE_CODE (gimple_call_fn (stmt)) == SSA_NAME)
>            {
>              tree orig_fn = gimple_call_fn (stmt);
> Index: gcc/tree-ssa-sccvn.c
> ===================================================================
> --- gcc/tree-ssa-sccvn.c        2011-04-18 10:18:49.000000000 +0100
> +++ gcc/tree-ssa-sccvn.c        2011-04-18 10:18:54.000000000 +0100
> @@ -911,7 +911,10 @@ copy_reference_ops_from_call (gimple cal
>   memset (&temp, 0, sizeof (temp));
>   temp.type = gimple_call_return_type (call);
>   temp.opcode = CALL_EXPR;
> -  temp.op0 = gimple_call_fn (call);
> +  if (gimple_call_internal_p (call))
> +    temp.op0 = NULL_TREE;
> +  else
> +    temp.op0 = gimple_call_fn (call);

That will now value-number all internal calls the same.  A safe change
would be to trivially value-number internal calls, like with


and a similar change in tree-ssa-pre.c:can_value_number_call.


>   temp.op1 = gimple_call_chain (call);
>   temp.off = -1;
>   VEC_safe_push (vn_reference_op_s, heap, *result, &temp);
> Index: gcc/tree-ssa-structalias.c
> ===================================================================
> --- gcc/tree-ssa-structalias.c  2011-04-18 10:18:49.000000000 +0100
> +++ gcc/tree-ssa-structalias.c  2011-04-18 10:18:54.000000000 +0100
> @@ -4026,6 +4026,8 @@ get_fi_for_callee (gimple call)
>  {
>   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.  */
> @@ -4319,6 +4321,7 @@ find_func_aliases (gimple origt)
>            /* 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)))
> Index: gcc/value-prof.c
> ===================================================================
> --- gcc/value-prof.c    2011-04-18 10:18:49.000000000 +0100
> +++ gcc/value-prof.c    2011-04-18 10:18:54.000000000 +0100
> @@ -1258,6 +1258,9 @@ gimple_ic_transform (gimple stmt)
>   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;
> @@ -1649,6 +1652,7 @@ gimple_indirect_call_to_profile (gimple
>   tree callee;
>
>   if (gimple_code (stmt) != GIMPLE_CALL
> +      || gimple_call_internal_p (stmt)
>       || gimple_call_fndecl (stmt) != NULL_TREE)
>     return;
>
> Index: gcc/lto-streamer-in.c
> ===================================================================
> --- gcc/lto-streamer-in.c       2011-04-18 10:18:49.000000000 +0100
> +++ gcc/lto-streamer-in.c       2011-04-18 10:18:54.000000000 +0100
> @@ -1063,7 +1063,13 @@ input_gimple_stmt (struct lto_input_bloc
>            }
>        }
>       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:
> Index: gcc/lto-streamer-out.c
> ===================================================================
> --- gcc/lto-streamer-out.c      2011-04-18 10:18:49.000000000 +0100
> +++ gcc/lto-streamer-out.c      2011-04-18 10:18:54.000000000 +0100
> @@ -1760,7 +1760,12 @@ output_gimple_stmt (struct output_block
>          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:

The rest looks ok.

Richard.

Comments

Richard Sandiford April 18, 2011, 1:37 p.m. UTC | #1
Richard Guenther <richard.guenther@gmail.com> writes:
>> +/* Expand a call to internal function FN.  ARGS is an array of the
>> +   function's arguments and LHS is where the result should be stored.  */
>> +
>> +void
>> +expand_internal_call (enum internal_fn fn, tree lhs, tree *args)
>> +{
>> +  internal_fn_expanders[(int) fn] (lhs, args);
>
> Can you use an interface that simply takes a gimple statement?
> We want to eventually transistion to expanding directly from them
> without re-creating a GENERIC call-expr for 4.7.

OK.

>>  /* 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;
>> +  gcc_gimple_checking_assert (!gimple_call_internal_p (gs));
>
> I'm not sure this is the best fallback but it's certainly safe ;)
> I think we should return NULL for internal fns, that is sure to trap
> also for non-checking-enabled builds.

trap == segfault?  We'd do that either way on most machines.
But yeah, if you think we should be able to call this for internal
functions, I'll change it.

>> +  return gs->gimple_call.u.fntype;
>>  }
>>
>>  /* Set the type of the function called by GS to FNTYPE.  */
>> @@ -2019,7 +2049,8 @@ gimple_call_fntype (const_gimple gs)
>>  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;
>>  }
>>
>>
>> @@ -2029,8 +2060,12 @@ gimple_call_set_fntype (gimple gs, tree
>>  static inline tree
>>  gimple_call_fn (const_gimple gs)
>>  {
>> +  tree op;
>> +
>>   GIMPLE_CHECK (gs, GIMPLE_CALL);
>> -  return gimple_op (gs, 1);
>> +  op = gimple_op (gs, 1);
>> +  gcc_gimple_checking_assert (op != NULL_TREE);
>
> Hmmm... probably good for your time of development but please remove
> this.  Rather add a hunk to verify_gimple_call that checks that internal
> fns have a NULL fn.

Well, TBH, I had it there because I thought it was a better interface.
I don't think it ever triggered during testing.  But again, if you think
it should be OK to call this for internal functions, I'll change it.

>> +/* 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);
>> +  gs->gsbase.subcode |= GF_CALL_INTERNAL;
>> +  gimple_set_op (gs, 1, NULL_TREE);
>
> can we instead assert this?  We shouldn't ever change a calls state
> from non-internal to internal (or vice-versa).

OK.

>> +  gs->gimple_call.u.internal_fn = fn;
>> +}
>> +
>> +
>>  /* Set FN to be the function called by call statement GS.  */
>>
>>  static inline void
>>  gimple_call_set_fn (gimple gs, tree fn)
>>  {
>>   GIMPLE_CHECK (gs, GIMPLE_CALL);
>> +  gs->gsbase.subcode &= ~GF_CALL_INTERNAL;
>
> Likewise.  Or just let the stmt verifier catch it.

Which is better?  I gather from the above that it's better not to
assert in these accessors.

>> +/* 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_internal_p (c2)
>> +           && operand_equal_p (gimple_call_fn (c1), gimple_call_fn (c2), 0));
>
> After a patch I am about to commit in a sec. the following for comparing
> gimple_call_fn would be better:
>
>   gimple_call_fn (c1) == gimple_call_fn (c2)
>   || (gimple_call_fndecl (c1)
>      && gimple_call_fndecl (c1) == gimple_call_fndecl (c2))
>
> because we have several forms of specifying a function-decl.

OK.

>> -  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);
>
> This code looks like it could be simplified by using gimple_copy, a
> loop to set arguments and gimple_set_num_ops.  That of course looks
> unrelated to your change, but it makes apparent that sth is wrong with
> this function ;)

Wouldn't that allocate unnecessary ops (as many as the original statement
had, rather than the number that have been kept)?  But like you say,
it seems unrelated to this patch, so I'd like to leave it be if that's OK.

>> Index: gcc/expr.c
>> ===================================================================
>> --- gcc/expr.c  2011-04-18 10:18:49.000000000 +0100
>> +++ gcc/expr.c  2011-04-18 10:18:54.000000000 +0100
>> @@ -8521,10 +8521,15 @@ expand_expr_real_1 (tree exp, rtx target
>>          enum machine_mode pmode;
>>
>>          /* Get the signedness to be used for this variable.  Ensure we get
>> -            the same mode we got when the variable was declared.  */
>> +            the same mode we got when the variable was declared.
>> +
>> +            Note that calls to internal functions do not result in a
>> +            call instruction, so promote_function_mode is not meaningful
>> +            in that case.  */
>>          if (code == SSA_NAME
>>              && (g = SSA_NAME_DEF_STMT (ssa_name))
>> -             && gimple_code (g) == GIMPLE_CALL)
>> +             && gimple_code (g) == GIMPLE_CALL
>> +             && !gimple_call_internal_p (g))
>
> I'm not sure we'll never need to do promotions for internal functions
> but at least for now this looks ok, and we can think of how to deal
> with this when it's necessary.  Of course it looks fishy that you
> even arrive here ...?

Yeah, I don't think we did arrive here.  I was trying to fix all uses of
gimple_call_fn and gimple_call_fntype by inspection.  I can assert instead
if you think it really shouldn't ever happen.

>> Index: gcc/gimple-low.c
>> ===================================================================
>> --- gcc/gimple-low.c    2011-04-18 10:18:49.000000000 +0100
>> +++ gcc/gimple-low.c    2011-04-18 10:18:54.000000000 +0100
>> @@ -224,6 +224,8 @@ gimple_check_call_args (gimple stmt, tre
>>   /* Get argument types for verification.  */
>>   if (fndecl)
>>     parms = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
>> +  else if (gimple_call_internal_p (stmt))
>> +    parms = NULL_TREE;
>
> Instead do a
>
>   /* Calls to internal functions always match their signature.  */
>   if (gimple_call_internal_p (stmt))
>     return true;
>
> early.

OK.

>>  verify_gimple_call (gimple stmt)
>>  {
>> -  tree fn = gimple_call_fn (stmt);
>> +  tree fn;
>>   tree fntype, fndecl;
>>   unsigned i;
>>
>> -  if (!is_gimple_call_addr (fn))
>> +  if (!gimple_call_internal_p (stmt))
>
> Please do not re-indent most of the function but instead do an upfront
> conditionl verification for internal calls:
>
>   if (gimple_call_internal_p (stmt))
>     {
>       if (gimple_op (stmt, 1) != NULL_TREE)
>         {
>           error ("non-NULL function in gimple internal call");
>           return true;
>         }
>       return false;
>     }

But what about stuff like:

  if (gimple_call_lhs (stmt)
      && (!is_gimple_lvalue (gimple_call_lhs (stmt))
	  || verify_types_in_gimple_reference (gimple_call_lhs (stmt), true)))
    {
      error ("invalid LHS in gimple call");
      return true;
    }

I left quite a few bits of the function out of the reindentation because
they seemed to apply (or be neutral) for internal functions.
>
>>     {
>> -      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))
>> -    {
>> -      error ("non-function in gimple call");
>> -      return true;
>> +      fn = gimple_call_fn (stmt);
>> +      if (!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))
>> +       {
>> +         error ("non-function in gimple call");
>> +         return true;
>> +       }
>> +      fntype = gimple_call_fntype (stmt);
>> +      if (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
>> +            void * call results.
>> +            ???  Java is completely off.  Especially with functions
>> +            returning java.lang.Object.
>> +            For now simply allow arbitrary pointer type conversions.  */
>> +         && !(POINTER_TYPE_P (TREE_TYPE (gimple_call_lhs (stmt)))
>> +              && POINTER_TYPE_P (TREE_TYPE (fntype))))
>> +       {
>> +         error ("invalid conversion in gimple call");
>> +         debug_generic_stmt (TREE_TYPE (gimple_call_lhs (stmt)));
>> +         debug_generic_stmt (TREE_TYPE (fntype));
>> +         return true;
>> +       }
>>     }
>>
>>    fndecl = gimple_call_fndecl (stmt);
>> @@ -3086,24 +3106,6 @@ verify_gimple_call (gimple stmt)
>>       return true;
>>     }
>>
>> -  fntype = gimple_call_fntype (stmt);
>> -  if (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
>> -        void * call results.
>> -        ???  Java is completely off.  Especially with functions
>> -        returning java.lang.Object.
>> -        For now simply allow arbitrary pointer type conversions.  */
>> -      && !(POINTER_TYPE_P (TREE_TYPE (gimple_call_lhs (stmt)))
>> -          && POINTER_TYPE_P (TREE_TYPE (fntype))))
>> -    {
>> -      error ("invalid conversion in gimple call");
>> -      debug_generic_stmt (TREE_TYPE (gimple_call_lhs (stmt)));
>> -      debug_generic_stmt (TREE_TYPE (fntype));
>> -      return true;
>> -    }
>> -
>>   if (gimple_call_chain (stmt)
>>       && !is_gimple_val (gimple_call_chain (stmt)))
>>     {
>> @@ -3121,7 +3123,7 @@ verify_gimple_call (gimple stmt)
>>          error ("static chain in indirect gimple call");
>>          return true;
>>        }
>> -      fn = TREE_OPERAND (fn, 0);
>> +      fn = TREE_OPERAND (gimple_call_fn (stmt), 0);
>>
>>       if (!DECL_STATIC_CHAIN (fn))
>>        {
>> @@ -7436,6 +7438,8 @@ do_warn_unused_result (gimple_seq seq)
>>        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
>> Index: gcc/tree-eh.c
>> ===================================================================
>> --- gcc/tree-eh.c       2011-04-18 10:18:49.000000000 +0100
>> +++ gcc/tree-eh.c       2011-04-18 10:18:54.000000000 +0100
>> @@ -2743,7 +2743,7 @@ same_handler_p (gimple_seq oneh, gimple_
>>       || 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)
>
> Why do we even end up here?  internal function calls should never throw,
> maybe that is what you need to adjust?

We didn't end up here for internal functions.  It was just a case of
using the new API rather than continuing to do a lower-level check.

>> @@ -565,8 +571,14 @@ print_expr_hash_elt (FILE * stream, cons
>>         {
>>           size_t i;
>>           size_t nargs = element->expr.ops.call.nargs;
>> +          gimple fn_from;
>>
>> -          print_generic_expr (stream, element->expr.ops.call.fn, 0);
>> +          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);
>
> 2nd time, can you add a print_gimple_call_target () function instead?

OK.

>> Index: gcc/tree-ssa-sccvn.c
>> ===================================================================
>> --- gcc/tree-ssa-sccvn.c        2011-04-18 10:18:49.000000000 +0100
>> +++ gcc/tree-ssa-sccvn.c        2011-04-18 10:18:54.000000000 +0100
>> @@ -911,7 +911,10 @@ copy_reference_ops_from_call (gimple cal
>>   memset (&temp, 0, sizeof (temp));
>>   temp.type = gimple_call_return_type (call);
>>   temp.opcode = CALL_EXPR;
>> -  temp.op0 = gimple_call_fn (call);
>> +  if (gimple_call_internal_p (call))
>> +    temp.op0 = NULL_TREE;
>> +  else
>> +    temp.op0 = gimple_call_fn (call);
>
> That will now value-number all internal calls the same.  A safe change
> would be to trivially value-number internal calls, like with
>
> Index: gcc/tree-ssa-sccvn.c
> ===================================================================
> --- gcc/tree-ssa-sccvn.c        (revision 172640)
> +++ gcc/tree-ssa-sccvn.c        (working copy)
> @@ -3125,7 +3125,8 @@ visit_use (tree use)
>           /* ???  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);
>
> and a similar change in tree-ssa-pre.c:can_value_number_call.

We really should be able to treat calls to pure internal functions
like calls to pure "real" functions though.

TBH, I think this is an example of why trying to so hard to avoid a tree
code for the internal function is working against us.  Most of the patch
feels like sprinkling hacks around the code base just because we don't
have a real tree representation for what we're calling.  Any new code
that handles calls is going to have to remember to make the same
distinction.

The tree I suggested was an INTERNAL_FUNCTION node, with the appropriate
type and number attached.  The nodes could be shared, rather than be
one-per-call.  If we had that, then I think this, and a lot of the other
places I'm touching, would Just Work.

Richard
Richard Biener April 18, 2011, 3:19 p.m. UTC | #2
On Mon, Apr 18, 2011 at 3:37 PM, Richard Sandiford
<richard.sandiford@linaro.org> wrote:
> Richard Guenther <richard.guenther@gmail.com> writes:
>>> +/* Expand a call to internal function FN.  ARGS is an array of the
>>> +   function's arguments and LHS is where the result should be stored.  */
>>> +
>>> +void
>>> +expand_internal_call (enum internal_fn fn, tree lhs, tree *args)
>>> +{
>>> +  internal_fn_expanders[(int) fn] (lhs, args);
>>
>> Can you use an interface that simply takes a gimple statement?
>> We want to eventually transistion to expanding directly from them
>> without re-creating a GENERIC call-expr for 4.7.
>
> OK.
>
>>>  /* 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;
>>> +  gcc_gimple_checking_assert (!gimple_call_internal_p (gs));
>>
>> I'm not sure this is the best fallback but it's certainly safe ;)
>> I think we should return NULL for internal fns, that is sure to trap
>> also for non-checking-enabled builds.
>
> trap == segfault?  We'd do that either way on most machines.
> But yeah, if you think we should be able to call this for internal
> functions, I'll change it.

Yes, we should be able to call it, but deal with NULL returns.

>>> +  return gs->gimple_call.u.fntype;
>>>  }
>>>
>>>  /* Set the type of the function called by GS to FNTYPE.  */
>>> @@ -2019,7 +2049,8 @@ gimple_call_fntype (const_gimple gs)
>>>  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;
>>>  }
>>>
>>>
>>> @@ -2029,8 +2060,12 @@ gimple_call_set_fntype (gimple gs, tree
>>>  static inline tree
>>>  gimple_call_fn (const_gimple gs)
>>>  {
>>> +  tree op;
>>> +
>>>   GIMPLE_CHECK (gs, GIMPLE_CALL);
>>> -  return gimple_op (gs, 1);
>>> +  op = gimple_op (gs, 1);
>>> +  gcc_gimple_checking_assert (op != NULL_TREE);
>>
>> Hmmm... probably good for your time of development but please remove
>> this.  Rather add a hunk to verify_gimple_call that checks that internal
>> fns have a NULL fn.
>
> Well, TBH, I had it there because I thought it was a better interface.
> I don't think it ever triggered during testing.  But again, if you think
> it should be OK to call this for internal functions, I'll change it.

Yep, see above

>>> +/* 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);
>>> +  gs->gsbase.subcode |= GF_CALL_INTERNAL;
>>> +  gimple_set_op (gs, 1, NULL_TREE);
>>
>> can we instead assert this?  We shouldn't ever change a calls state
>> from non-internal to internal (or vice-versa).
>
> OK.
>
>>> +  gs->gimple_call.u.internal_fn = fn;
>>> +}
>>> +
>>> +
>>>  /* Set FN to be the function called by call statement GS.  */
>>>
>>>  static inline void
>>>  gimple_call_set_fn (gimple gs, tree fn)
>>>  {
>>>   GIMPLE_CHECK (gs, GIMPLE_CALL);
>>> +  gs->gsbase.subcode &= ~GF_CALL_INTERNAL;
>>
>> Likewise.  Or just let the stmt verifier catch it.
>
> Which is better?  I gather from the above that it's better not to
> assert in these accessors.

Both are setters, not accessors.  I think for setters we want asserts
to be able to spot errors where they creep in.  They are also not called
that often.

>>> +/* 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_internal_p (c2)
>>> +           && operand_equal_p (gimple_call_fn (c1), gimple_call_fn (c2), 0));
>>
>> After a patch I am about to commit in a sec. the following for comparing
>> gimple_call_fn would be better:
>>
>>   gimple_call_fn (c1) == gimple_call_fn (c2)
>>   || (gimple_call_fndecl (c1)
>>      && gimple_call_fndecl (c1) == gimple_call_fndecl (c2))
>>
>> because we have several forms of specifying a function-decl.
>
> OK.
>
>>> -  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);
>>
>> This code looks like it could be simplified by using gimple_copy, a
>> loop to set arguments and gimple_set_num_ops.  That of course looks
>> unrelated to your change, but it makes apparent that sth is wrong with
>> this function ;)
>
> Wouldn't that allocate unnecessary ops (as many as the original statement
> had, rather than the number that have been kept)?  But like you say,
> it seems unrelated to this patch, so I'd like to leave it be if that's OK.

True, it would allocate some waste, but it would make the code
clearer and 100% safe as to what needs to be copied and how.
Leaving it be is ok.

>>> Index: gcc/expr.c
>>> ===================================================================
>>> --- gcc/expr.c  2011-04-18 10:18:49.000000000 +0100
>>> +++ gcc/expr.c  2011-04-18 10:18:54.000000000 +0100
>>> @@ -8521,10 +8521,15 @@ expand_expr_real_1 (tree exp, rtx target
>>>          enum machine_mode pmode;
>>>
>>>          /* Get the signedness to be used for this variable.  Ensure we get
>>> -            the same mode we got when the variable was declared.  */
>>> +            the same mode we got when the variable was declared.
>>> +
>>> +            Note that calls to internal functions do not result in a
>>> +            call instruction, so promote_function_mode is not meaningful
>>> +            in that case.  */
>>>          if (code == SSA_NAME
>>>              && (g = SSA_NAME_DEF_STMT (ssa_name))
>>> -             && gimple_code (g) == GIMPLE_CALL)
>>> +             && gimple_code (g) == GIMPLE_CALL
>>> +             && !gimple_call_internal_p (g))
>>
>> I'm not sure we'll never need to do promotions for internal functions
>> but at least for now this looks ok, and we can think of how to deal
>> with this when it's necessary.  Of course it looks fishy that you
>> even arrive here ...?
>
> Yeah, I don't think we did arrive here.  I was trying to fix all uses of
> gimple_call_fn and gimple_call_fntype by inspection.  I can assert instead
> if you think it really shouldn't ever happen.

An assert would indeed be better (I suppose promote_function_mode
happily does some random stuff with a NULL function type?)

>>> Index: gcc/gimple-low.c
>>> ===================================================================
>>> --- gcc/gimple-low.c    2011-04-18 10:18:49.000000000 +0100
>>> +++ gcc/gimple-low.c    2011-04-18 10:18:54.000000000 +0100
>>> @@ -224,6 +224,8 @@ gimple_check_call_args (gimple stmt, tre
>>>   /* Get argument types for verification.  */
>>>   if (fndecl)
>>>     parms = TYPE_ARG_TYPES (TREE_TYPE (fndecl));
>>> +  else if (gimple_call_internal_p (stmt))
>>> +    parms = NULL_TREE;
>>
>> Instead do a
>>
>>   /* Calls to internal functions always match their signature.  */
>>   if (gimple_call_internal_p (stmt))
>>     return true;
>>
>> early.
>
> OK.
>
>>>  verify_gimple_call (gimple stmt)
>>>  {
>>> -  tree fn = gimple_call_fn (stmt);
>>> +  tree fn;
>>>   tree fntype, fndecl;
>>>   unsigned i;
>>>
>>> -  if (!is_gimple_call_addr (fn))
>>> +  if (!gimple_call_internal_p (stmt))
>>
>> Please do not re-indent most of the function but instead do an upfront
>> conditionl verification for internal calls:
>>
>>   if (gimple_call_internal_p (stmt))
>>     {
>>       if (gimple_op (stmt, 1) != NULL_TREE)
>>         {
>>           error ("non-NULL function in gimple internal call");
>>           return true;
>>         }
>>       return false;
>>     }
>
> But what about stuff like:
>
>  if (gimple_call_lhs (stmt)
>      && (!is_gimple_lvalue (gimple_call_lhs (stmt))
>          || verify_types_in_gimple_reference (gimple_call_lhs (stmt), true)))
>    {
>      error ("invalid LHS in gimple call");
>      return true;
>    }
>
> I left quite a few bits of the function out of the reindentation because
> they seemed to apply (or be neutral) for internal functions.

Ah, indeed.  Maybe just re-order bits so the early out for internal
functions works?

>>
>>>     {
>>> -      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))
>>> -    {
>>> -      error ("non-function in gimple call");
>>> -      return true;
>>> +      fn = gimple_call_fn (stmt);
>>> +      if (!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))
>>> +       {
>>> +         error ("non-function in gimple call");
>>> +         return true;
>>> +       }
>>> +      fntype = gimple_call_fntype (stmt);
>>> +      if (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
>>> +            void * call results.
>>> +            ???  Java is completely off.  Especially with functions
>>> +            returning java.lang.Object.
>>> +            For now simply allow arbitrary pointer type conversions.  */
>>> +         && !(POINTER_TYPE_P (TREE_TYPE (gimple_call_lhs (stmt)))
>>> +              && POINTER_TYPE_P (TREE_TYPE (fntype))))
>>> +       {
>>> +         error ("invalid conversion in gimple call");
>>> +         debug_generic_stmt (TREE_TYPE (gimple_call_lhs (stmt)));
>>> +         debug_generic_stmt (TREE_TYPE (fntype));
>>> +         return true;
>>> +       }
>>>     }
>>>
>>>    fndecl = gimple_call_fndecl (stmt);
>>> @@ -3086,24 +3106,6 @@ verify_gimple_call (gimple stmt)
>>>       return true;
>>>     }
>>>
>>> -  fntype = gimple_call_fntype (stmt);
>>> -  if (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
>>> -        void * call results.
>>> -        ???  Java is completely off.  Especially with functions
>>> -        returning java.lang.Object.
>>> -        For now simply allow arbitrary pointer type conversions.  */
>>> -      && !(POINTER_TYPE_P (TREE_TYPE (gimple_call_lhs (stmt)))
>>> -          && POINTER_TYPE_P (TREE_TYPE (fntype))))
>>> -    {
>>> -      error ("invalid conversion in gimple call");
>>> -      debug_generic_stmt (TREE_TYPE (gimple_call_lhs (stmt)));
>>> -      debug_generic_stmt (TREE_TYPE (fntype));
>>> -      return true;
>>> -    }
>>> -
>>>   if (gimple_call_chain (stmt)
>>>       && !is_gimple_val (gimple_call_chain (stmt)))
>>>     {
>>> @@ -3121,7 +3123,7 @@ verify_gimple_call (gimple stmt)
>>>          error ("static chain in indirect gimple call");
>>>          return true;
>>>        }
>>> -      fn = TREE_OPERAND (fn, 0);
>>> +      fn = TREE_OPERAND (gimple_call_fn (stmt), 0);
>>>
>>>       if (!DECL_STATIC_CHAIN (fn))
>>>        {
>>> @@ -7436,6 +7438,8 @@ do_warn_unused_result (gimple_seq seq)
>>>        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
>>> Index: gcc/tree-eh.c
>>> ===================================================================
>>> --- gcc/tree-eh.c       2011-04-18 10:18:49.000000000 +0100
>>> +++ gcc/tree-eh.c       2011-04-18 10:18:54.000000000 +0100
>>> @@ -2743,7 +2743,7 @@ same_handler_p (gimple_seq oneh, gimple_
>>>       || 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)
>>
>> Why do we even end up here?  internal function calls should never throw,
>> maybe that is what you need to adjust?
>
> We didn't end up here for internal functions.  It was just a case of
> using the new API rather than continuing to do a lower-level check.

Ok, that's fine with me.

>>> @@ -565,8 +571,14 @@ print_expr_hash_elt (FILE * stream, cons
>>>         {
>>>           size_t i;
>>>           size_t nargs = element->expr.ops.call.nargs;
>>> +          gimple fn_from;
>>>
>>> -          print_generic_expr (stream, element->expr.ops.call.fn, 0);
>>> +          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);
>>
>> 2nd time, can you add a print_gimple_call_target () function instead?
>
> OK.
>
>>> Index: gcc/tree-ssa-sccvn.c
>>> ===================================================================
>>> --- gcc/tree-ssa-sccvn.c        2011-04-18 10:18:49.000000000 +0100
>>> +++ gcc/tree-ssa-sccvn.c        2011-04-18 10:18:54.000000000 +0100
>>> @@ -911,7 +911,10 @@ copy_reference_ops_from_call (gimple cal
>>>   memset (&temp, 0, sizeof (temp));
>>>   temp.type = gimple_call_return_type (call);
>>>   temp.opcode = CALL_EXPR;
>>> -  temp.op0 = gimple_call_fn (call);
>>> +  if (gimple_call_internal_p (call))
>>> +    temp.op0 = NULL_TREE;
>>> +  else
>>> +    temp.op0 = gimple_call_fn (call);
>>
>> That will now value-number all internal calls the same.  A safe change
>> would be to trivially value-number internal calls, like with
>>
>> Index: gcc/tree-ssa-sccvn.c
>> ===================================================================
>> --- gcc/tree-ssa-sccvn.c        (revision 172640)
>> +++ gcc/tree-ssa-sccvn.c        (working copy)
>> @@ -3125,7 +3125,8 @@ visit_use (tree use)
>>           /* ???  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);
>>
>> and a similar change in tree-ssa-pre.c:can_value_number_call.
>
> We really should be able to treat calls to pure internal functions
> like calls to pure "real" functions though.
>
> TBH, I think this is an example of why trying to so hard to avoid a tree
> code for the internal function is working against us.  Most of the patch
> feels like sprinkling hacks around the code base just because we don't
> have a real tree representation for what we're calling.  Any new code
> that handles calls is going to have to remember to make the same
> distinction.

Most code either doesn't care about the callee or only handles calls
to a specific function and thus already checks whether gimple_call_fndecl ()
returns NULL.  The patch doesn't feel like it adds hacks everywhere,
instead it is quite small (smaller than I thought at least).

> The tree I suggested was an INTERNAL_FUNCTION node, with the appropriate
> type and number attached.  The nodes could be shared, rather than be
> one-per-call.  If we had that, then I think this, and a lot of the other
> places I'm touching, would Just Work.

I don't think this is in any way better than untyped calls.  Instead you'd
run into a lot of generic tree stuff (think of walk_tree, tree_code_size, etc.).
Also it would be semantically the same as an untyped call, just in
non-tuple form which is backward from what we want. To avoid that you'd
have to at least do INTERNAL_UNARY_FUNCTION, INTERNAL_BINARY_FUNCTION,
etc. (and have to stop at TERNARY).
You would have exactly the same type issues.  Also you would have
the issue that such things can only be const, not pure, or you'd go
the non-tuples way again.

So no, an INTERNAL_FUNCTION tree code would be ugly.  If at all
the alternative would be a LOAD_LANE and STORE_LANE tree code,
which I already said is working against canonicalizing memory references
and thus would be need to split into a load via a MEM_REF and then
a SHUFFLE or PERMUTE unary op that works on a SSA name
array that was loaded.  But as you said, that's a lot more work ;)

Richard.

> Richard
>
Richard Biener April 19, 2011, 1:29 p.m. UTC | #3
On Tue, Apr 19, 2011 at 3:14 PM, Richard Sandiford
<richard.sandiford@linaro.org> wrote:
> Richard Guenther <richard.guenther@gmail.com> writes:
>>> We really should be able to treat calls to pure internal functions
>>> like calls to pure "real" functions though.
>>>
>>> TBH, I think this is an example of why trying to so hard to avoid a tree
>>> code for the internal function is working against us.  Most of the patch
>>> feels like sprinkling hacks around the code base just because we don't
>>> have a real tree representation for what we're calling.  Any new code
>>> that handles calls is going to have to remember to make the same
>>> distinction.
>>
>> Most code either doesn't care about the callee or only handles calls
>> to a specific function and thus already checks whether gimple_call_fndecl ()
>> returns NULL.  The patch doesn't feel like it adds hacks everywhere,
>> instead it is quite small (smaller than I thought at least).
>
> OK, just testing your resolve :-)
>
> Here's a revised patch, updated this morning.  Main changes:
>
> * gimple_call_fn and gimple_call_fntype can be called unconditionally.
>  They return null for internal functions.
>
> * gimple_call_set_fn, gimple_call_set_fntype and gimple_call_set_fndecl
>  assert that the call isn't to an internal function.
>
> * gimple_call_set_internal_fn asserts the opposite.
>
> * verify_gimple_call checks internalness vs. nullness.  This shouldn't
>  trigger with the asserts above, but it's there Just In Case.
>
> * I've not moved code around in verify_gimple_call but added null checks
>  instead.
>
> * Some other places also check for nullness rather than specifically for
>  internal calls.
>
> * gimple_call_same_target_p uses the expression you suggested.
>
> * The expanders now take the gimple call statement rather than the
>  tree operands.
>
> * I've made the value-numbering changes you suggested.
>
> I didn't factor out the dumping code because the three cases are
> doing different things (one raw dump, one pretty dump to a buffer,
> and one pretty dump to a file).
>
> Tested on x86_64-linux-gnu and arm-linux-gnueabi.  OK to install?

Ok with me.  Diego, can you have a 2nd eye?

Thanks,
Richard.

> Richard
>
>
> gcc/
>        * 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.
>
> Index: gcc/Makefile.in
> ===================================================================
> --- gcc/Makefile.in     2011-04-19 10:27:24.000000000 +0100
> +++ gcc/Makefile.in     2011-04-19 10:56:05.000000000 +0100
> @@ -893,6 +893,8 @@ RTL_ERROR_H = $(RTL_H) $(DIAGNOSTIC_CORE
>  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) \
> @@ -901,8 +903,8 @@ TREE_H = tree.h all-tree.def tree.def c-
>  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
> @@ -1274,6 +1276,7 @@ OBJS-common = \
>        init-regs.o \
>        input.o \
>        integrate.o \
> +       internal-fn.o \
>        intl.o \
>        ira.o \
>        ira-build.o \
> @@ -2759,6 +2762,8 @@ tree-object-size.o: tree-object-size.c $
>    $(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) \
> Index: gcc/internal-fn.def
> ===================================================================
> --- /dev/null   2011-03-23 08:42:11.268792848 +0000
> +++ gcc/internal-fn.def 2011-04-19 11:04:08.000000000 +0100
> @@ -0,0 +1,39 @@
> +/* 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.  */
> Index: gcc/internal-fn.h
> ===================================================================
> --- /dev/null   2011-03-23 08:42:11.268792848 +0000
> +++ gcc/internal-fn.h   2011-04-19 10:56:05.000000000 +0100
> @@ -0,0 +1,51 @@
> +/* 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 stmt);
> +
> +#endif
> Index: gcc/internal-fn.c
> ===================================================================
> --- /dev/null   2011-03-23 08:42:11.268792848 +0000
> +++ gcc/internal-fn.c   2011-04-19 11:04:08.000000000 +0100
> @@ -0,0 +1,64 @@
> +/* 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);
> +}
> Index: gcc/gimple.h
> ===================================================================
> --- gcc/gimple.h        2011-04-19 10:27:24.000000000 +0100
> +++ gcc/gimple.h        2011-04-19 10:56:05.000000000 +0100
> @@ -30,6 +30,7 @@ #define GCC_GIMPLE_H
>  #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;
> @@ -103,6 +104,7 @@ enum gf_mask {
>     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
> @@ -407,7 +409,10 @@ struct GTY(()) gimple_statement_call
>   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
> @@ -821,6 +826,8 @@ #define gimple_build_debug_bind(var,val,
>
>  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);
> @@ -865,6 +872,7 @@ gimple_seq gimple_seq_alloc (void);
>  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);
> @@ -2006,13 +2014,36 @@ gimple_call_set_lhs (gimple gs, tree lhs
>  }
>
>
> +/* 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.  */
> @@ -2021,7 +2052,8 @@ gimple_call_fntype (const_gimple gs)
>  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;
>  }
>
>
> @@ -2052,6 +2084,7 @@ gimple_call_fn_ptr (const_gimple gs)
>  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);
>  }
>
> @@ -2062,16 +2095,29 @@ gimple_call_set_fn (gimple gs, tree 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
> @@ -2102,6 +2148,9 @@ gimple_call_return_type (const_gimple gs
>  {
>   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);
> Index: gcc/gimple.c
> ===================================================================
> --- gcc/gimple.c        2011-04-19 10:27:24.000000000 +0100
> +++ gcc/gimple.c        2011-04-19 10:56:05.000000000 +0100
> @@ -277,6 +277,59 @@ gimple_build_call (tree fn, unsigned nar
>  }
>
>
> +/* 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
>  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_gimpl
>   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 @@ canonicalize_cond_expr_cond (tree t)
>  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,
>     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));
> Index: gcc/cfgexpand.c
> ===================================================================
> --- gcc/cfgexpand.c     2011-04-19 10:27:24.000000000 +0100
> +++ gcc/cfgexpand.c     2011-04-19 10:56:05.000000000 +0100
> @@ -1837,10 +1837,16 @@ expand_gimple_cond (basic_block bb, gimp
>  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);
> @@ -1890,6 +1896,7 @@ expand_call_stmt (gimple 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
> Index: gcc/expr.c
> ===================================================================
> --- gcc/expr.c  2011-04-19 10:56:05.000000000 +0100
> +++ gcc/expr.c  2011-04-19 10:56:05.000000000 +0100
> @@ -8528,9 +8528,12 @@ expand_expr_real_1 (tree exp, rtx target
>          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);
> Index: gcc/gimple-fold.c
> ===================================================================
> --- gcc/gimple-fold.c   2011-04-19 10:27:24.000000000 +0100
> +++ gcc/gimple-fold.c   2011-04-19 11:05:02.000000000 +0100
> @@ -1473,7 +1473,8 @@ gimple_fold_call (gimple_stmt_iterator *
>
>   /* 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
>       && gimple_call_addr_fndecl (OBJ_TYPE_REF_EXPR (callee)) != NULL_TREE)
>     {
>       gimple_call_set_fn (stmt, OBJ_TYPE_REF_EXPR (callee));
> @@ -2873,7 +2874,13 @@ gimple_fold_stmt_to_constant_1 (gimple s
>
>     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)))
> Index: gcc/gimple-low.c
> ===================================================================
> --- gcc/gimple-low.c    2011-04-19 10:27:24.000000000 +0100
> +++ gcc/gimple-low.c    2011-04-19 10:56:05.000000000 +0100
> @@ -219,6 +219,10 @@ gimple_check_call_args (gimple stmt, tre
>   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.  */
> Index: gcc/gimple-pretty-print.c
> ===================================================================
> --- gcc/gimple-pretty-print.c   2011-04-19 10:27:24.000000000 +0100
> +++ gcc/gimple-pretty-print.c   2011-04-19 10:56:05.000000000 +0100
> @@ -616,8 +616,12 @@ dump_gimple_call (pretty_printer *buffer
>
>   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, ", ");
> @@ -637,7 +641,10 @@ dump_gimple_call (pretty_printer *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, ')');
> Index: gcc/ipa-prop.c
> ===================================================================
> --- gcc/ipa-prop.c      2011-04-19 10:27:24.000000000 +0100
> +++ gcc/ipa-prop.c      2011-04-19 10:56:05.000000000 +0100
> @@ -1406,6 +1406,8 @@ ipa_analyze_call_uses (struct cgraph_nod
>  {
>   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)
> Index: gcc/tree-cfg.c
> ===================================================================
> --- gcc/tree-cfg.c      2011-04-19 10:27:24.000000000 +0100
> +++ gcc/tree-cfg.c      2011-04-19 10:56:05.000000000 +0100
> @@ -3046,16 +3046,35 @@ verify_gimple_call (gimple stmt)
>   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;
> @@ -3087,7 +3106,8 @@ verify_gimple_call (gimple stmt)
>     }
>
>   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
> @@ -7436,6 +7456,8 @@ do_warn_unused_result (gimple_seq seq)
>        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
> Index: gcc/tree-eh.c
> ===================================================================
> --- gcc/tree-eh.c       2011-04-19 10:27:24.000000000 +0100
> +++ gcc/tree-eh.c       2011-04-19 10:56:05.000000000 +0100
> @@ -2743,7 +2743,7 @@ same_handler_p (gimple_seq oneh, gimple_
>       || 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;
>
> Index: gcc/tree-ssa-ccp.c
> ===================================================================
> --- gcc/tree-ssa-ccp.c  2011-04-19 10:27:24.000000000 +0100
> +++ gcc/tree-ssa-ccp.c  2011-04-19 10:56:05.000000000 +0100
> @@ -1722,6 +1722,11 @@ ccp_fold_stmt (gimple_stmt_iterator *gsi
>            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
> Index: gcc/tree-ssa-dom.c
> ===================================================================
> --- gcc/tree-ssa-dom.c  2011-04-19 10:27:24.000000000 +0100
> +++ gcc/tree-ssa-dom.c  2011-04-19 10:56:05.000000000 +0100
> @@ -64,7 +64,7 @@ struct hashable_expr
>     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;
>  };
>
> @@ -258,7 +258,7 @@ initialize_hash_element (gimple stmt, tr
>
>       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;
> @@ -422,8 +422,8 @@ hashable_expr_equal_p (const struct hash
>
>         /* 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)
> @@ -503,9 +503,15 @@ iterative_hash_hashable_expr (const stru
>       {
>         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);
>       }
> @@ -565,8 +571,14 @@ print_expr_hash_elt (FILE * stream, cons
>         {
>           size_t i;
>           size_t nargs = element->expr.ops.call.nargs;
> +          gimple fn_from;
>
> -          print_generic_expr (stream, element->expr.ops.call.fn, 0);
> +          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++)
>             {
> Index: gcc/tree-ssa-pre.c
> ===================================================================
> --- gcc/tree-ssa-pre.c  2011-04-19 10:27:24.000000000 +0100
> +++ gcc/tree-ssa-pre.c  2011-04-19 10:56:05.000000000 +0100
> @@ -2657,11 +2657,13 @@ compute_antic (void)
>  }
>
>  /* 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;
> @@ -4384,6 +4386,8 @@ eliminate (void)
>            {
>              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
> Index: gcc/tree-ssa-sccvn.c
> ===================================================================
> --- gcc/tree-ssa-sccvn.c        2011-04-19 10:27:24.000000000 +0100
> +++ gcc/tree-ssa-sccvn.c        2011-04-19 10:56:05.000000000 +0100
> @@ -3125,7 +3125,8 @@ visit_use (tree use)
>          /* ???  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);
> Index: gcc/tree-ssa-structalias.c
> ===================================================================
> --- gcc/tree-ssa-structalias.c  2011-04-19 10:27:24.000000000 +0100
> +++ gcc/tree-ssa-structalias.c  2011-04-19 10:56:05.000000000 +0100
> @@ -4026,6 +4026,8 @@ get_fi_for_callee (gimple call)
>  {
>   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.  */
> @@ -4319,6 +4321,7 @@ find_func_aliases (gimple origt)
>            /* 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)))
> Index: gcc/value-prof.c
> ===================================================================
> --- gcc/value-prof.c    2011-04-19 10:27:24.000000000 +0100
> +++ gcc/value-prof.c    2011-04-19 10:56:05.000000000 +0100
> @@ -1258,6 +1258,9 @@ gimple_ic_transform (gimple stmt)
>   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;
> @@ -1649,6 +1652,7 @@ gimple_indirect_call_to_profile (gimple
>   tree callee;
>
>   if (gimple_code (stmt) != GIMPLE_CALL
> +      || gimple_call_internal_p (stmt)
>       || gimple_call_fndecl (stmt) != NULL_TREE)
>     return;
>
> Index: gcc/lto-streamer-in.c
> ===================================================================
> --- gcc/lto-streamer-in.c       2011-04-19 10:27:24.000000000 +0100
> +++ gcc/lto-streamer-in.c       2011-04-19 10:56:05.000000000 +0100
> @@ -1063,7 +1063,13 @@ input_gimple_stmt (struct lto_input_bloc
>            }
>        }
>       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:
> Index: gcc/lto-streamer-out.c
> ===================================================================
> --- gcc/lto-streamer-out.c      2011-04-19 10:27:24.000000000 +0100
> +++ gcc/lto-streamer-out.c      2011-04-19 10:56:05.000000000 +0100
> @@ -1760,7 +1760,12 @@ output_gimple_stmt (struct output_block
>          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:
>
Diego Novillo April 19, 2011, 1:40 p.m. UTC | #4
On 04/19/2011 09:14 AM, Richard Sandiford wrote:

> Tested on x86_64-linux-gnu and arm-linux-gnueabi.  OK to install?

OK with me.  Thanks for your patience.  Just a couple of minor nits below


> +  extern const int internal_fn_flags_array[];
> +  return internal_fn_flags_array[(int) fn];
> +}
> +
> +extern void expand_internal_call (gimple stmt);

s/stmt//

>   static inline tree
>   gimple_call_addr_fndecl (const_tree fn)
>   {
> -  if (TREE_CODE (fn) == ADDR_EXPR)
> +  if (fn&&  TREE_CODE (fn) == ADDR_EXPR)

Can't tell if thunderbird is being silly about spaces again or you are 
missing a space before '&&'.  I see it in a couple other places below, 
so I think it's just TB.


Diego.
diff mbox

Patch

Index: gcc/tree-ssa-sccvn.c
===================================================================
--- gcc/tree-ssa-sccvn.c        (revision 172640)
+++ gcc/tree-ssa-sccvn.c        (working copy)
@@ -3125,7 +3125,8 @@  visit_use (tree use)
          /* ???  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);