This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[IFUNC] PATCH: Add an ifunc attribute


Hi,

Here is a patch to add ifunc attribute, including many testscases
in C and C++.  Any comments?

Thanks.


H.J.
-----
gcc/

2009-06-28  H.J. Lu  <hongjiu.lu@intel.com>

	PR c/40528
	* c-common.c (handle_ifunc_attribute): New.
	(c_common_attribute_table): Add "ifunc".

	* c-decl.c (merge_decls): Only allow IFUNC attribute on
	definition.  Merge the IFUNC information.
	(start_function): Use function_return_type to get function
	return type.
	(store_parm_decls_newstyle): Don't warn omitted parameter name
	on IFUNC function.
	(finish_function): Issue an error if control reaches end of
	IFUNC function.
	(c_write_global_declarations_1): Add an argument to indicate
	global scope.  Issue an error for undefined IFUNC function.
	(c_write_global_declarations): Updated.

	* c-typeck.c (c_finish_return): Use function_return_type to
	get function return type.
	* gimplify.c (gimplify_return_expr): Likewise.
	* stmt.c (expand_return): Likewise.

	* cgraphunit.c (cgraph_finalize_function): Don't allow
	parameter usage in ifunc function.
	(process_function_and_variable_attributes): Check ifunc attribute.

	* tree-cfg.c (verify_gimple_return): Use function_return_type
	to get function return type.
	(execute_warn_function_return): Issue an error if control
	reaches end of ifunc function.

	* tree.h (tree_decl_with_vis): Add ifunc_flag.
	(DECL_IS_IFUNC): New.

	* varasm.c (default_binds_local_p_1): Return false for IFUNC
	function.

	* config/elfos.h (ASM_DECLARE_FUNCTION_NAME): Output
	"gnu_indirect_function" instead of "function" for IFUNC
	function.

	* doc/extend.texi: Document ifunc attribute.

gcc/cp/

2009-06-28  H.J. Lu  <hongjiu.lu@intel.com>

	PR c/40528
	* typeck.c (cp_build_unary_op): Take the address of non-static
	member function for IFUNC member function.
	(check_return_expr): Use function_return_type to get function
	return type.

	* decl.c (check_function_type): Change return type to bool.
	Return true if the function type is changed to void. 
	(wrapup_globals_for_namespace): Check undefined IFUNC symbols. 
	(duplicate_decls): Only allow IFUNC attribute on definition.
	Merge the IFUNC information.
	(start_preparsed_function): Use function_return_type to build
	the return declaration for the function if its type isn't
	changed to void.
	(finish_function): Use function_return_type to get function
	return type.  Issue an error if control reaches end of IFUNC
	function.

gcc/testsuite/

2009-06-28  H.J. Lu  <hongjiu.lu@intel.com>

	PR c/40528
	* g++.dg/torture/ifunc-1.C: New.
	* g++.dg/torture/ifunc-2.C: Likewise.
	* g++.dg/torture/ifunc-3.C: Likewise.
	* g++.dg/torture/ifunc-4.C: Likewise.
	* g++.dg/torture/ifunc-5.C: Likewise.
	* g++.dg/torture/ifunc-6.C: Likewise.
	* g++.dg/torture/ifunc-7.C: Likewise.
	* g++.dg/torture/ifunc-8.C: Likewise.
	* g++.dg/torture/ifunc-9.C: Likewise.
	* g++.dg/torture/ifunc-10.C: Likewise.
	* g++.dg/torture/ifunc-11.C: Likewise.
	* g++.dg/torture/ifunc-12.C: Likewise.
	* g++.dg/torture/ifunc-13.C: Likewise.
	* g++.dg/torture/ifunc-14.C: Likewise.
	* g++.dg/torture/ifunc-15.C: Likewise.
	* g++.dg/torture/ifunc-16.C: Likewise.
	* g++.dg/torture/ifunc-17.C: Likewise.
	* g++.dg/torture/ifunc-18.C: Likewise.
	* g++.dg/torture/ifunc-19.C: Likewise.
	* g++.dg/torture/ifunc-20.C: Likewise.
	* gcc.dg/torture/ifunc-1.c: Likewise.
	* gcc.dg/torture/ifunc-2.c: Likewise.
	* gcc.dg/torture/ifunc-3.c: Likewise.
	* gcc.dg/torture/ifunc-4.c: Likewise.
	* gcc.dg/torture/ifunc-5.c: Likewise.
	* gcc.dg/torture/ifunc-6.c: Likewise.
	* gcc.dg/torture/ifunc-7.c: Likewise.
	* gcc.dg/torture/ifunc-8.c: Likewise.
	* gcc.dg/torture/ifunc-9.c: Likewise.
	* gcc.dg/torture/ifunc-10.c: Likewise.
	* gcc.dg/torture/ifunc-11.c: Likewise.
	* gcc.dg/torture/ifunc-12.c: Likewise.
	* gcc.dg/torture/ifunc-13.c: Likewise.
	* gcc.dg/torture/ifunc-14.c: Likewise.
	* gcc.dg/torture/ifunc-15.c: Likewise.
	* gcc.dg/torture/ifunc-16.c: Likewise.
	* gcc.dg/torture/ifunc-17.c: Likewise.
	* gcc.dg/torture/ifunc-18.c: Likewise.

Index: gcc/doc/extend.texi
===================================================================
--- gcc/doc/extend.texi	(.../fsf/trunk)	(revision 6328)
+++ gcc/doc/extend.texi	(.../branches/ifunc)	(revision 6328)
@@ -1924,6 +1924,28 @@ is not defined in the same translation u
 
 Not all target machines support this attribute.
 
+@item ifunc
+@cindex @code{ifunc} attribute
+The @code{ifunc} attribute only applies to function definition, which
+causes the definition to be emitted as an indirec function.  For
+instance,
+
+@smallexample
+int f (int) __attribute__ ((ifunc));
+@end smallexample
+
+defines @samp{f} as an indirect function.  @samp{f} should return a
+pointer to the actual function that should be executed as @samp{f}.
+@samp{f} is called only by dynamic linker without any arguments. 
+It is an error if @samp{f} is undefined in the same translation unit,
+or any parameters are used inside @samp{f}, or the return type isn't
+a pointer to @samp{f}.
+
+See @code{STT_GNU_IFUNC} specified in @file{ifunc.txt} at
+@uref{http://groups.google.com/group/generic-abi/files}.
+
+Not all target machines support this attribute.
+
 @item aligned (@var{alignment})
 @cindex @code{aligned} attribute
 This attribute specifies a minimum alignment for the function,
Index: gcc/tree.h
===================================================================
--- gcc/tree.h	(.../fsf/trunk)	(revision 6328)
+++ gcc/tree.h	(.../branches/ifunc)	(revision 6328)
@@ -2951,10 +2951,11 @@ struct GTY(()) tree_decl_with_vis {
  unsigned visibility_specified : 1;
  /* Belong to FUNCTION_DECL exclusively.  */
  unsigned init_priority_p:1;
+ unsigned ifunc_flag:1;
 
  /* Belongs to VAR_DECL exclusively.  */
  ENUM_BITFIELD(tls_model) tls_model : 3;
- /* 14 unused bits. */
+ /* 13 unused bits. */
 };
 
 /* In a VAR_DECL that's static,
@@ -3021,6 +3022,10 @@ extern void decl_fini_priority_insert (t
    libraries.  */
 #define MAX_RESERVED_INIT_PRIORITY 100
 
+/* For a FUNCTION_DECL, nonzero if it is an IFUNC symbol.  */
+#define DECL_IS_IFUNC(NODE) \
+  (DECL_WITH_VIS_CHECK(NODE)->decl_with_vis.ifunc_flag)
+
 /* In a VAR_DECL, the model to use if the data should be allocated from
    thread-local storage.  */
 #define DECL_TLS_MODEL(NODE) (VAR_DECL_CHECK (NODE)->decl_with_vis.tls_model)
@@ -5304,4 +5309,16 @@ more_const_call_expr_args_p (const const
   for ((arg) = first_const_call_expr_arg ((call), &(iter)); (arg);	\
        (arg) = next_const_call_expr_arg (&(iter)))
 
+/* Get the function return type inside function body.  Return a pointer
+   to the function for IFUNC function.  */
+
+static inline tree
+function_return_type (const_tree decl)
+{
+  if (DECL_IS_IFUNC (decl))
+    return build_pointer_type (TREE_TYPE (decl));
+  else
+    return TREE_TYPE (TREE_TYPE (decl));
+}
+
 #endif  /* GCC_TREE_H  */
Index: gcc/cgraphunit.c
===================================================================
--- gcc/cgraphunit.c	(.../fsf/trunk)	(revision 6328)
+++ gcc/cgraphunit.c	(.../branches/ifunc)	(revision 6328)
@@ -529,8 +529,24 @@ cgraph_finalize_function (tree decl, boo
   if (!TREE_ASM_WRITTEN (decl))
     (*debug_hooks->deferred_inline_function) (decl);
 
+  /* Parameters in IFUNC function should never be used.  */
+  if (DECL_IS_IFUNC (decl))
+    {
+      tree parm;
+
+      for (parm = DECL_ARGUMENTS (decl);
+	   parm; parm = TREE_CHAIN (parm))
+	{
+	  if (TREE_USED (parm)
+	      && TREE_CODE (parm) == PARM_DECL
+	      && DECL_NAME (parm))
+	    error ("parameter %q+D used in indirect function %q+F",
+		   parm, decl);
+	}
+    }
+
   /* Possibly warn about unused parameters.  */
-  if (warn_unused_parameter)
+  else if (warn_unused_parameter)
     do_warn_unused_parameter (decl);
 
   if (!nested)
@@ -836,7 +852,8 @@ process_function_and_variable_attributes
   for (node = cgraph_nodes; node != first; node = node->next)
     {
       tree decl = node->decl;
-      if (lookup_attribute ("used", DECL_ATTRIBUTES (decl)))
+      if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (decl))
+	  || lookup_attribute ("used", DECL_ATTRIBUTES (decl)))
 	{
 	  mark_decl_referenced (decl);
 	  if (node->local.finalized)
Index: gcc/testsuite/gcc.dg/torture/ifunc-1.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-1.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-1.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,25 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+static int
+foo1 (int x) 
+{
+  return x;
+}
+
+int
+__attribute__ ((ifunc))
+foo (int)
+{ 
+  return foo1;
+}
+
+int
+bar (int i)
+{
+  return foo (i);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+foo, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+foo" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+foo, .gnu_indirect_function" } } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-5.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-5.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-5.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,24 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+void
+foo1 (void) 
+{
+}
+
+void
+__attribute__ ((ifunc))
+foo (void)
+{ 
+  return foo1;
+}
+
+void
+bar (void)
+{
+  foo ();
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+foo, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+foo" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+foo, .gnu_indirect_function" } } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-9.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-9.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-9.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+
+int
+__attribute__ ((ifunc))
+foo (int, int)
+{ 
+  return 1;
+}
+
+extern int bar1 (int, int);
+
+void
+__attribute__ ((ifunc))
+bar (int, int)
+{ 
+  return bar1;
+}
+
+/* { dg-warning "return makes pointer from integer without a cast" "" { target *-*-* } 7 } */
+/* { dg-warning "return from incompatible pointer type" "" { target *-*-* } 16 } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-12.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-12.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-12.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+void
+__attribute__ ((ifunc))
+foo (int, int)
+{ 
+}
+
+/* { dg-error "control reaches end of indirect function" ""  { target *-*-* } 7 } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-16.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-16.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-16.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,27 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+static int
+foo1 (int x) 
+{
+  return x;
+}
+
+static int foo (int);
+
+static int
+__attribute__ ((ifunc))
+foo (int x)
+{ 
+  return foo1;
+}
+
+int
+bar (int i)
+{
+  return foo (i);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+foo, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+foo" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+foo, .gnu_indirect_function" } } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-2.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-2.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-2.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,25 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+static int
+foo1 (int x) 
+{
+  return x;
+}
+
+static int
+__attribute__ ((ifunc))
+foo (int x)
+{ 
+  return foo1;
+}
+
+int
+bar (int i)
+{
+  return foo (i);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+foo, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+foo" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+foo, .gnu_indirect_function" } } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-6.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-6.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-6.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,5 @@
+/* { dg-do compile } */
+
+int __attribute__ ((ifunc)) foo (int);
+
+/* { dg-error "indirect function .foo. never defined" ""  { target *-*-* } 3 } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-13.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-13.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-13.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+static void
+__attribute__ ((ifunc))
+foo (int, int)
+{ 
+}
+
+/* { dg-error "control reaches end of indirect function" ""  { target *-*-* } 7 } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-17.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-17.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-17.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+
+extern int foo1 (int);
+extern int foo (int) __attribute__ ((ifunc));
+
+int
+foo (int)
+{ 
+  return foo1;
+}
+
+/* { dg-error "definition of function .foo. conflicts with" ""  { target *-*-* } 7 } */
+/* { dg-error "previous declaration of indirect function .foo. here" ""  { target *-*-* } 4 } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-3.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-3.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-3.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,25 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-fPIE -Wall -Wextra" } */
+
+static int
+foo1 (int x) 
+{
+  return x;
+}
+
+static int
+__attribute__ ((ifunc))
+foo (int)
+{ 
+  return foo1;
+}
+
+int
+bar (int i)
+{
+  return foo (i);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+foo, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+foo@PLT" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+foo, .gnu_indirect_function" } } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-7.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-7.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-7.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,5 @@
+/* { dg-do compile } */
+
+static int __attribute__ ((ifunc)) foo (int);
+
+/* { dg-error "indirect function .foo. never defined" ""  { target *-*-* } 3 } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-10.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-10.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-10.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+int
+__attribute__ ((ifunc))
+foo (int, int)
+{ 
+}
+
+/* { dg-error "control reaches end of indirect function" ""  { target *-*-* } 7 } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-14.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-14.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-14.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-options "-Wall -Wextra" } */
+
+static int
+__attribute__ ((ifunc))
+foo (int, int)
+{ 
+}
+
+/* { dg-error "control reaches end of indirect function" ""  { target *-*-* } 8 } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-18.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-18.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-18.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,27 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+static int
+foo1 (int x) 
+{
+  return x;
+}
+
+extern int foo (int) __attribute__ ((ifunc));
+
+int
+__attribute__ ((ifunc))
+foo (int)
+{ 
+  return foo1;
+}
+
+int
+bar (int i)
+{
+  return foo (i);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+foo, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+foo" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+foo, .gnu_indirect_function" } } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-4.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-4.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-4.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,25 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-fPIC -Wall -Wextra" } */
+
+static int
+foo1 (int x, int y) 
+{
+  return x - y;
+}
+
+int
+__attribute__ ((ifunc, visibility ("hidden")))
+foo (int, int y)
+{ 
+  return foo1;
+}
+
+int
+bar (int i)
+{
+  return foo (i, 1);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+foo, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+foo@PLT" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+foo, .gnu_indirect_function" } } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-8.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-8.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-8.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+
+extern int foo1 (int, int); 
+extern int foo2 (int, int); 
+
+int
+__attribute__ ((ifunc))
+foo (int x, int y)
+{ 
+  if ((x + y) == 34)
+    return foo1;
+  else
+    return foo2;
+}
+
+/* { dg-error "parameter .\[x|y\]. used in indirect function .foo." ""  { target *-*-* } 8 } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-11.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-11.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-11.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+static int
+__attribute__ ((ifunc))
+foo (int, int)
+{ 
+}
+
+/* { dg-error "control reaches end of indirect function" ""  { target *-*-* } 7 } */
Index: gcc/testsuite/gcc.dg/torture/ifunc-15.c
===================================================================
--- gcc/testsuite/gcc.dg/torture/ifunc-15.c	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/gcc.dg/torture/ifunc-15.c	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,27 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+static int
+foo1 (int x) 
+{
+  return x;
+}
+
+int
+__attribute__ ((ifunc))
+foo (int)
+{ 
+  return foo1;
+}
+
+extern int foo (int);
+
+int
+bar (int i)
+{
+  return foo (i);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+foo, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+foo" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+foo, .gnu_indirect_function" } } */
Index: gcc/testsuite/ChangeLog.ifunc
===================================================================
Index: gcc/testsuite/g++.dg/torture/ifunc-1.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-1.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-1.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,25 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+static int
+foo1 (int x) 
+{
+  return x;
+}
+
+int
+__attribute__ ((ifunc))
+foo (int)
+{ 
+  return foo1;
+}
+
+int
+bar (int i)
+{
+  return foo (i);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+_Z3fooi, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+_Z3fooi" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+_Z3fooi, .gnu_indirect_function" } } */
Index: gcc/testsuite/g++.dg/torture/ifunc-3.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-3.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-3.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,25 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-fPIE -Wall -Wextra" } */
+
+static int
+foo1 (int x) 
+{
+  return x;
+}
+
+static int
+__attribute__ ((ifunc))
+foo (int)
+{ 
+  return foo1;
+}
+
+int
+bar (int i)
+{
+  return foo (i);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+_ZL3fooi, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+_ZL3fooi@PLT" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+_ZL3fooi, .gnu_indirect_function" } } */
Index: gcc/testsuite/g++.dg/torture/ifunc-5.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-5.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-5.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,24 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+void
+foo1 (void) 
+{
+}
+
+void
+__attribute__ ((ifunc))
+foo (void)
+{ 
+  return foo1;
+}
+
+void
+bar (void)
+{
+  foo ();
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+_Z3foov, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+_Z3foov" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+_Z3foov, .gnu_indirect_function" } } */
Index: gcc/testsuite/g++.dg/torture/ifunc-7.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-7.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-7.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,5 @@
+/* { dg-do compile } */
+
+static int __attribute__ ((ifunc)) foo (int);
+
+/* { dg-error "indirect function .int foo.int.. never defined" ""  { target *-*-* } 3 } */
Index: gcc/testsuite/g++.dg/torture/ifunc-9.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-9.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-9.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+
+int
+__attribute__ ((ifunc))
+foo (int, int)
+{ 
+  return 1;
+}
+
+extern int bar1 (int, int);
+
+void
+__attribute__ ((ifunc))
+bar (int, int)
+{ 
+  return bar1;
+}
+
+/* { dg-error "invalid conversion from .int. to .int ....int, int.." ""  { target *-*-* } 7 } */
+/* { dg-error "invalid conversion from .int ....int, int.. to .void ....int, int.." ""  { target *-*-* } 16 } */
Index: gcc/testsuite/g++.dg/torture/ifunc-10.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-10.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-10.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+int
+__attribute__ ((ifunc))
+foo (int, int)
+{ 
+}
+
+/* { dg-error "control reaches end of indirect function" ""  { target *-*-* } 7 } */
Index: gcc/testsuite/g++.dg/torture/ifunc-12.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-12.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-12.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+void
+__attribute__ ((ifunc))
+foo (int, int)
+{ 
+}
+
+/* { dg-error "control reaches end of indirect function" ""  { target *-*-* } 7 } */
Index: gcc/testsuite/g++.dg/torture/ifunc-14.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-14.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-14.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,10 @@
+/* { dg-do compile } */
+/* { dg-options "-Wall -Wextra" } */
+
+static int
+__attribute__ ((ifunc))
+foo (int, int)
+{ 
+}
+
+/* { dg-error "control reaches end of indirect function" ""  { target *-*-* } 8 } */
Index: gcc/testsuite/g++.dg/torture/ifunc-16.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-16.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-16.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,48 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+class ifunc
+{
+private:
+  int foo1 (int);
+  int foo2 (float);
+
+public:
+  int foo (int);
+  int foo (float);
+  int bar (int);
+  int bar (float);
+};
+
+int
+__attribute__ ((ifunc))
+ifunc::foo (int)
+{ 
+  return &ifunc::foo1;
+}
+
+int
+__attribute__ ((ifunc))
+ifunc::foo (float)
+{ 
+  return &ifunc::foo2;
+}
+
+int
+ifunc::bar (int x)
+{
+  return foo (x);
+}
+
+int
+ifunc::bar (float x)
+{
+  return foo (x);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+_ZN5ifunc3fooEi, .function" } } */
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+_ZN5ifunc3fooEf, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+_ZN5ifunc3fooEi" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+_ZN5ifunc3fooEi, .gnu_indirect_function" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+_ZN5ifunc3fooEf, .gnu_indirect_function" } } */
Index: gcc/testsuite/g++.dg/torture/ifunc-18.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-18.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-18.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+
+class ifunc
+{
+private:
+  int foo1 (int);
+  int foo2 (int);
+  int i;
+
+public:
+  int foo (int);
+};
+
+int
+__attribute__ ((ifunc))
+ifunc::foo (int x)
+{ 
+  if ((x + i) == 34)
+    return &ifunc::foo1;
+  else
+    return &ifunc::foo2;
+}
+
+/* { dg-error "parameter .this. used in indirect function .int ifunc::foo.int.." ""  { target *-*-* } 16 } */
+/* { dg-error "parameter .x. used in indirect function .int ifunc::foo.int.." ""  { target *-*-* } 16 } */
Index: gcc/testsuite/g++.dg/torture/ifunc-2.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-2.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-2.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,25 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+static int
+foo1 (int x) 
+{
+  return x;
+}
+
+static int
+__attribute__ ((ifunc))
+foo (int x)
+{ 
+  return foo1;
+}
+
+int
+bar (int i)
+{
+  return foo (i);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+_ZL3fooi, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+_ZL3fooi" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+_ZL3fooi, .gnu_indirect_function" } } */
Index: gcc/testsuite/g++.dg/torture/ifunc-4.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-4.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-4.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,25 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-fPIC -Wall -Wextra" } */
+
+static int
+foo1 (int x, int y) 
+{
+  return x - y;
+}
+
+int
+__attribute__ ((ifunc, visibility ("hidden")))
+foo (int, int y)
+{ 
+  return foo1;
+}
+
+int
+bar (int i)
+{
+  return foo (i, 1);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+_Z3fooii, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+_Z3fooii@PLT" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+_Z3fooii, .gnu_indirect_function" } } */
Index: gcc/testsuite/g++.dg/torture/ifunc-6.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-6.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-6.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,5 @@
+/* { dg-do compile } */
+
+int __attribute__ ((ifunc)) foo (int);
+
+/* { dg-error "indirect function .int foo.int.. never defined" ""  { target *-*-* } 3 } */
Index: gcc/testsuite/g++.dg/torture/ifunc-8.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-8.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-8.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+
+static int
+foo1 (int x, int y) 
+{
+  return x + y;
+}
+
+static int
+foo2 (int x, int y) 
+{
+  return x - y;
+}
+
+int
+__attribute__ ((ifunc))
+foo (int x, int y)
+{ 
+  if ((x + y) == 34)
+    return foo1;
+  else
+    return foo2;
+}
+
+/* { dg-error "parameter .\[x|y\]. used in indirect function .int foo.int, int.." ""  { target *-*-* } 17 } */
Index: gcc/testsuite/g++.dg/torture/ifunc-11.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-11.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-11.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+static int
+__attribute__ ((ifunc))
+foo (int, int)
+{ 
+}
+
+/* { dg-error "control reaches end of indirect function" ""  { target *-*-* } 7 } */
Index: gcc/testsuite/g++.dg/torture/ifunc-13.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-13.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-13.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+static void
+__attribute__ ((ifunc))
+foo (int, int)
+{ 
+}
+
+/* { dg-error "control reaches end of indirect function" ""  { target *-*-* } 7 } */
Index: gcc/testsuite/g++.dg/torture/ifunc-15.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-15.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-15.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,46 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+class ifunc
+{
+private:
+  static int foo1 (int);
+  static int foo2 (float);
+
+public:
+  static int foo (int);
+  static int foo (float);
+};
+
+int
+__attribute__ ((ifunc))
+ifunc::foo (int)
+{ 
+  return &ifunc::foo1;
+}
+
+int
+__attribute__ ((ifunc))
+ifunc::foo (float)
+{ 
+  return &ifunc::foo2;
+}
+
+int
+bar (int x)
+{
+  return ifunc::foo (x);
+}
+
+int
+bar (float x)
+{
+  return ifunc::foo (x);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+_ZN5ifunc3fooEf, .function" } } */
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+_ZN5ifunc3fooEi, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+_ZN5ifunc3fooEi" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+_ZN5ifunc3fooEf" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+_ZN5ifunc3fooEi, .gnu_indirect_function" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+_ZN5ifunc3fooEf, .gnu_indirect_function" } } */
Index: gcc/testsuite/g++.dg/torture/ifunc-17.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-17.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-17.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,48 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+class ifunc
+{
+private:
+  void foo1 (int);
+  void foo2 (float);
+
+public:
+  void foo (int);
+  void foo (float);
+  void bar (int);
+  void bar (float);
+};
+
+void
+__attribute__ ((ifunc))
+ifunc::foo (int)
+{ 
+  return &ifunc::foo1;
+}
+
+void
+__attribute__ ((ifunc))
+ifunc::foo (float)
+{ 
+  return &ifunc::foo2;
+}
+
+void
+ifunc::bar (int x)
+{
+  foo (x);
+}
+
+void
+ifunc::bar (float x)
+{
+  foo (x);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+_ZN5ifunc3fooEi, .function" } } */
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+_ZN5ifunc3fooEf, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+_ZN5ifunc3fooEi" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+_ZN5ifunc3fooEf" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+_ZN5ifunc3fooEi, .gnu_indirect_function" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+_ZN5ifunc3fooEf, .gnu_indirect_function" } } */
Index: gcc/testsuite/g++.dg/torture/ifunc-19.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-19.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-19.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+
+extern int foo1 (int);
+extern int foo (int) __attribute__ ((ifunc));
+
+int
+foo (int)
+{ 
+  return foo1;
+}
+
+/* { dg-error "definition of function .int foo.int.. conflicts with" ""  { target *-*-* } 7 } */
+/* { dg-error "previous declaration of indirect function .int foo.int.. here" ""  { target *-*-* } 4 } */
+/* { dg-error "invalid conversion from .int ....int.. to .int." ""  { target *-*-* } 9 } */
+/* { dg-error "indirect function .int foo.int.. never defined" ""  { target *-*-* } 4 } */
Index: gcc/testsuite/g++.dg/torture/ifunc-20.C
===================================================================
--- gcc/testsuite/g++.dg/torture/ifunc-20.C	(.../fsf/trunk)	(revision 0)
+++ gcc/testsuite/g++.dg/torture/ifunc-20.C	(.../branches/ifunc)	(revision 6328)
@@ -0,0 +1,27 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+static int
+foo1 (int x) 
+{
+  return x;
+}
+
+extern int foo (int) __attribute__ ((ifunc));
+
+int
+__attribute__ ((ifunc))
+foo (int)
+{ 
+  return foo1;
+}
+
+int
+bar (int i)
+{
+  return foo (i);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+_Z3fooi, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+_Z3fooi" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+_Z3fooi, .gnu_indirect_function" } } */
Index: gcc/cp/typeck.c
===================================================================
--- gcc/cp/typeck.c	(.../fsf/trunk)	(revision 6328)
+++ gcc/cp/typeck.c	(.../branches/ifunc)	(revision 6328)
@@ -4712,9 +4712,20 @@ cp_build_unary_op (enum tree_code code, 
 		return error_mark_node;
 	      }
 
-	    type = build_ptrmem_type (context_for_name_lookup (t),
-				      TREE_TYPE (t));
-	    t = make_ptrmem_cst (type, TREE_OPERAND (arg, 1));
+	    if (current_function_decl != NULL
+		&& DECL_IS_IFUNC (current_function_decl)
+		&& DECL_NONSTATIC_MEMBER_FUNCTION_P (current_function_decl))
+	      {
+		/* In IFUNC member function, we take the address of
+		   another non-static member function.  */
+		t = build_address (t);
+	      }
+	    else
+	      {
+		type = build_ptrmem_type (context_for_name_lookup (t),
+					  TREE_TYPE (t));
+		t = make_ptrmem_cst (type, TREE_OPERAND (arg, 1));
+	      }
 	    return t;
 	  }
 
@@ -7103,7 +7114,7 @@ check_return_expr (tree retval, bool *no
         function.  */
      && same_type_p ((TYPE_MAIN_VARIANT (TREE_TYPE (retval))),
                      (TYPE_MAIN_VARIANT
-                      (TREE_TYPE (TREE_TYPE (current_function_decl)))))
+                      (function_return_type (current_function_decl))))
      /* And the returned value must be non-volatile.  */
      && ! TYPE_VOLATILE (TREE_TYPE (retval)));
      
@@ -7129,7 +7140,7 @@ check_return_expr (tree retval, bool *no
   else
     {
       /* The type the function is declared to return.  */
-      tree functype = TREE_TYPE (TREE_TYPE (current_function_decl));
+      tree functype = function_return_type (current_function_decl);
       int flags = LOOKUP_NORMAL | LOOKUP_ONLYCONVERTING;
 
       /* The functype's return type will have been set to void, if it
@@ -7145,7 +7156,7 @@ check_return_expr (tree retval, bool *no
           /* The variable must not have the `volatile' qualifier.  */
 	  && !(cp_type_quals (TREE_TYPE (retval)) & TYPE_QUAL_VOLATILE)
 	  /* The return type must be a class type.  */
-	  && CLASS_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))))
+	  && CLASS_TYPE_P (functype))
 	flags = flags | LOOKUP_PREFER_RVALUE;
 
       /* First convert the value to the function's return type, then
Index: gcc/cp/decl.c
===================================================================
--- gcc/cp/decl.c	(.../fsf/trunk)	(revision 6328)
+++ gcc/cp/decl.c	(.../branches/ifunc)	(revision 6328)
@@ -85,7 +85,7 @@ static void layout_var_decl (tree);
 static tree check_initializer (tree, tree, int, tree *);
 static void make_rtl_for_nonlocal_decl (tree, tree, const char *);
 static void save_function_data (tree);
-static void check_function_type (tree, tree);
+static bool check_function_type (tree, tree);
 static void finish_constructor_body (void);
 static void begin_destructor_body (void);
 static void finish_destructor_body (void);
@@ -817,9 +817,23 @@ wrapup_globals_for_namespace (tree name_
   struct cp_binding_level *level = NAMESPACE_LEVEL (name_space);
   VEC(tree,gc) *statics = level->static_decls;
   tree *vec = VEC_address (tree, statics);
+  tree decl;
   int len = VEC_length (tree, statics);
   int last_time = (data != 0);
 
+  /* Check undefined IFUNC symbols.  */
+  for (decl = level->names; decl; decl = TREE_CHAIN (decl))
+    if (TREE_CODE (decl) == FUNCTION_DECL
+	&& DECL_INITIAL (decl) == 0
+	&& DECL_EXTERNAL (decl)
+	&& ! TREE_NO_WARNING (decl)
+	&& DECL_IS_IFUNC (decl))
+    {
+      error_at (input_location,
+		"indirect function %q+F never defined", decl);
+      TREE_NO_WARNING (decl) = 1;
+    }
+
   if (last_time)
     {
       check_global_declarations (vec, len);
@@ -1607,6 +1621,25 @@ duplicate_decls (tree newdecl, tree oldd
 	  error ("deleted definition of %qD", newdecl);
 	  error ("after previous declaration %q+D", olddecl);
 	}
+
+      /* The IFUNC information must be on definition since it may
+	 change the function return type inside the function body.  */
+      if (new_defines_function
+	  && DECL_IS_IFUNC (olddecl)
+	  && !DECL_IS_IFUNC (newdecl))
+	{
+	  error ("definition of function %q+D conflicts with",
+		 newdecl);
+	  error ("previous declaration of indirect function %q+D here",
+		 olddecl);
+	  return error_mark_node;
+	}
+
+      /* Merge the IFUNC information.  */
+      if (DECL_IS_IFUNC (olddecl))
+	DECL_IS_IFUNC (newdecl) = 1;
+      else if (DECL_IS_IFUNC (newdecl))
+	DECL_IS_IFUNC (olddecl) = 1;
     }
 
   /* Deal with C++: must preserve virtual function table size.  */
@@ -11435,9 +11468,10 @@ lookup_enumerator (tree enumtype, tree n
 }
 
 
-/* We're defining DECL.  Make sure that it's type is OK.  */
+/* We're defining DECL.  Make sure that it's type is OK.  Return TRUE
+   if its type is changed to void.  */
 
-static void
+static bool
 check_function_type (tree decl, tree current_function_parms)
 {
   tree fntype = TREE_TYPE (decl);
@@ -11447,7 +11481,7 @@ check_function_type (tree decl, tree cur
   require_complete_types_for_parms (current_function_parms);
 
   if (dependent_type_p (return_type))
-    return;
+    return false;
   if (!COMPLETE_OR_VOID_TYPE_P (return_type)
       || (TYPE_FOR_JAVA (return_type) && MAYBE_CLASS_TYPE_P (return_type)))
     {
@@ -11468,9 +11502,13 @@ check_function_type (tree decl, tree cur
       TREE_TYPE (decl)
 	= build_exception_variant (fntype,
 				   TYPE_RAISES_EXCEPTIONS (TREE_TYPE (decl)));
+      return true;
     }
   else
-    abstract_virtuals_error (decl, TREE_TYPE (fntype));
+    {
+      abstract_virtuals_error (decl, TREE_TYPE (fntype));
+      return false;
+    }
 }
 
 /* Create the FUNCTION_DECL for a function definition.
@@ -11613,11 +11651,14 @@ start_preparsed_function (tree decl1, tr
 
   /* Make sure the parameter and return types are reasonable.  When
      you declare a function, these types can be incomplete, but they
-     must be complete when you define the function.  */
-  check_function_type (decl1, current_function_parms);
+     must be complete when you define the function.
 
-  /* Build the return declaration for the function.  */
-  restype = TREE_TYPE (fntype);
+     Use function_return_type to build the return declaration for the
+     function if FNTYPE isn't changed to void.  */
+  if (check_function_type (decl1, current_function_parms))
+    restype = TREE_TYPE (fntype);
+  else
+    restype = function_return_type (decl1);
   if (DECL_RESULT (decl1) == NULL_TREE)
     {
       tree resdecl;
@@ -12224,7 +12265,7 @@ tree
 finish_function (int flags)
 {
   tree fndecl = current_function_decl;
-  tree fntype, ctype = NULL_TREE;
+  tree restype, ctype = NULL_TREE;
   int inclass_inline = (flags & 2) != 0;
   int nested;
 
@@ -12246,7 +12287,7 @@ finish_function (int flags)
     }
 
   nested = function_depth > 1;
-  fntype = TREE_TYPE (fndecl);
+  restype = function_return_type (fndecl);
 
   /*  TREE_READONLY (fndecl) = 1;
       This caused &foo to be of type ptr-to-const-function
@@ -12340,7 +12381,7 @@ finish_function (int flags)
       if (r != error_mark_node
 	  /* This is only worth doing for fns that return in memory--and
 	     simpler, since we don't have to worry about promoted modes.  */
-	  && aggregate_value_p (TREE_TYPE (TREE_TYPE (fndecl)), fndecl)
+	  && aggregate_value_p (restype, fndecl)
 	  /* Only allow this for variables declared in the outer scope of
 	     the function so we know that their lifetime always ends with a
 	     return; see g++.dg/opt/nrv6.C.  We could be more flexible if
@@ -12368,9 +12409,10 @@ finish_function (int flags)
     save_function_data (fndecl);
 
   /* Complain if there's just no return statement.  */
-  if (warn_return_type
-      && TREE_CODE (TREE_TYPE (fntype)) != VOID_TYPE
-      && !dependent_type_p (TREE_TYPE (fntype))
+  if ((warn_return_type
+       || DECL_IS_IFUNC (fndecl))
+      && TREE_CODE (restype) != VOID_TYPE
+      && !dependent_type_p (restype)
       && !current_function_returns_value && !current_function_returns_null
       /* Don't complain if we abort or throw.  */
       && !current_function_returns_abnormally
@@ -12380,8 +12422,11 @@ finish_function (int flags)
       && !DECL_CONSTRUCTOR_P (fndecl)
       && !DECL_DESTRUCTOR_P (fndecl))
     {
-      warning (OPT_Wreturn_type,
- 	       "no return statement in function returning non-void");
+      if (DECL_IS_IFUNC (fndecl))
+	error ("control reaches end of indirect function");
+      else
+	warning (OPT_Wreturn_type,
+		 "no return statement in function returning non-void");
       TREE_NO_WARNING (fndecl) = 1;
     }
 
Index: gcc/cp/ChangeLog.ifunc
===================================================================
Index: gcc/c-decl.c
===================================================================
--- gcc/c-decl.c	(.../fsf/trunk)	(revision 6328)
+++ gcc/c-decl.c	(.../branches/ifunc)	(revision 6328)
@@ -2353,6 +2353,23 @@ merge_decls (tree newdecl, tree olddecl,
 	    DECL_ABSTRACT_ORIGIN (newdecl)
 	      = DECL_ABSTRACT_ORIGIN (olddecl);
 	}
+      else if (DECL_IS_IFUNC (olddecl)
+	       && !DECL_IS_IFUNC (newdecl))
+	{
+	  /* The IFUNC information must be on definition since it
+	     may change the function return type inside the function
+	     body.  */
+	  error ("definition of function %q+D conflicts with",
+		 newdecl);
+	  error ("previous declaration of indirect function %q+D here",
+		 olddecl);
+	}
+
+      /* Merge the IFUNC information.  */
+      if (DECL_IS_IFUNC (olddecl))
+	DECL_IS_IFUNC (newdecl) = 1;
+      else if (DECL_IS_IFUNC (newdecl))
+	DECL_IS_IFUNC (olddecl) = 1;
     }
 
   extern_changed = DECL_EXTERNAL (olddecl) && !DECL_EXTERNAL (newdecl);
@@ -7443,7 +7460,7 @@ start_function (struct c_declspecs *decl
   push_scope ();
   declare_parm_level ();
 
-  restype = TREE_TYPE (TREE_TYPE (current_function_decl));
+  restype = function_return_type (current_function_decl);
   resdecl = build_decl (loc, RESULT_DECL, NULL_TREE, restype);
   DECL_ARTIFICIAL (resdecl) = 1;
   DECL_IGNORED_P (resdecl) = 1;
@@ -7496,7 +7513,11 @@ store_parm_decls_newstyle (tree fndecl, 
 	    warn_if_shadowing (decl);
 	}
       else
-	error ("%Jparameter name omitted", decl);
+	{
+	  /* Parameters in IFUNC function are never used.  */
+	  if (!DECL_IS_IFUNC (current_function_decl))
+	    error ("%Jparameter name omitted", decl);
+	}
     }
 
   /* Record the parameter list in the function declaration.  */
@@ -7938,8 +7959,9 @@ finish_function (void)
   finish_fname_decls ();
 
   /* Complain if there's just no return statement.  */
-  if (warn_return_type
-      && TREE_CODE (TREE_TYPE (TREE_TYPE (fndecl))) != VOID_TYPE
+  if ((warn_return_type
+       || DECL_IS_IFUNC (fndecl))
+      && TREE_CODE (function_return_type (fndecl)) != VOID_TYPE
       && !current_function_returns_value && !current_function_returns_null
       /* Don't complain if we are no-return.  */
       && !current_function_returns_abnormally
@@ -7951,8 +7973,11 @@ finish_function (void)
          optimize out static functions.  */
       && !TREE_PUBLIC (fndecl))
     {
-      warning (OPT_Wreturn_type,
-	       "no return statement in function returning non-void");
+      if (DECL_IS_IFUNC (fndecl))
+	error ("control reaches end of indirect function");
+      else
+	warning (OPT_Wreturn_type,
+		 "no return statement in function returning non-void");
       TREE_NO_WARNING (fndecl) = 1;
     }
 
@@ -9317,7 +9342,7 @@ finish_declspecs (struct c_declspecs *sp
    GLOBALS.  */
 
 static void
-c_write_global_declarations_1 (tree globals)
+c_write_global_declarations_1 (tree globals, bool ext_scope)
 {
   tree decl;
   bool reconsider;
@@ -9327,15 +9352,30 @@ c_write_global_declarations_1 (tree glob
     {
       /* Check for used but undefined static functions using the C
 	 standard's definition of "used", and set TREE_NO_WARNING so
-	 that check_global_declarations doesn't repeat the check.  */
+	 that check_global_declarations doesn't repeat the check.
+
+	 Issue an error if an IFUNC function is undefined.  */
       if (TREE_CODE (decl) == FUNCTION_DECL
 	  && DECL_INITIAL (decl) == 0
-	  && DECL_EXTERNAL (decl)
-	  && !TREE_PUBLIC (decl)
-	  && C_DECL_USED (decl))
+	  && DECL_EXTERNAL (decl))
 	{
-	  pedwarn (input_location, 0, "%q+F used but never defined", decl);
-	  TREE_NO_WARNING (decl) = 1;
+	  if (DECL_IS_IFUNC (decl))
+	    {
+	      if (ext_scope || !TREE_PUBLIC (decl))
+		{
+		  error_at (input_location,
+			    "indirect function %q+F never defined",
+			    decl);
+		  TREE_NO_WARNING (decl) = 1;
+		}
+	    }
+	  else if (!TREE_PUBLIC (decl)
+		   && C_DECL_USED (decl))
+	    {
+	      pedwarn (input_location, 0, "%q+F used but never defined",
+		       decl);
+	      TREE_NO_WARNING (decl) = 1;
+	    }
 	}
 
       wrapup_global_declaration_1 (decl);
@@ -9402,8 +9442,8 @@ c_write_global_declarations (void)
   /* Process all file scopes in this compilation, and the external_scope,
      through wrapup_global_declarations and check_global_declarations.  */
   for (t = all_translation_units; t; t = TREE_CHAIN (t))
-    c_write_global_declarations_1 (BLOCK_VARS (DECL_INITIAL (t)));
-  c_write_global_declarations_1 (BLOCK_VARS (ext_block));
+    c_write_global_declarations_1 (BLOCK_VARS (DECL_INITIAL (t)), false);
+  c_write_global_declarations_1 (BLOCK_VARS (ext_block), true);
 
   /* We're done parsing; proceed to optimize and emit assembly.
      FIXME: shouldn't be the front end's responsibility to call this.  */
Index: gcc/c-typeck.c
===================================================================
--- gcc/c-typeck.c	(.../fsf/trunk)	(revision 6328)
+++ gcc/c-typeck.c	(.../branches/ifunc)	(revision 6328)
@@ -8035,7 +8035,7 @@ c_finish_goto_ptr (location_t loc, tree 
 tree
 c_finish_return (location_t loc, tree retval, tree origtype)
 {
-  tree valtype = TREE_TYPE (TREE_TYPE (current_function_decl)), ret_stmt;
+  tree valtype = function_return_type (current_function_decl), ret_stmt;
   bool no_warning = false;
   bool npc = false;
 
Index: gcc/gimplify.c
===================================================================
--- gcc/gimplify.c	(.../fsf/trunk)	(revision 6328)
+++ gcc/gimplify.c	(.../branches/ifunc)	(revision 6328)
@@ -1306,7 +1306,7 @@ gimplify_return_expr (tree stmt, gimple_
       return GS_ALL_DONE;
     }
 
-  if (VOID_TYPE_P (TREE_TYPE (TREE_TYPE (current_function_decl))))
+  if (VOID_TYPE_P (function_return_type (current_function_decl)))
     result_decl = NULL_TREE;
   else
     {
Index: gcc/varasm.c
===================================================================
--- gcc/varasm.c	(.../fsf/trunk)	(revision 6328)
+++ gcc/varasm.c	(.../branches/ifunc)	(revision 6328)
@@ -6488,8 +6488,13 @@ default_binds_local_p_1 (const_tree exp,
 {
   bool local_p;
 
+  /* IFUNC DECLs are always global since it has to go through PLT even
+     for local definitions.  */
+  if (TREE_CODE (exp) == FUNCTION_DECL
+      && DECL_IS_IFUNC (exp))
+    local_p = false;
   /* A non-decl is an entry in the constant pool.  */
-  if (!DECL_P (exp))
+  else if (!DECL_P (exp))
     local_p = true;
   /* Weakrefs may not bind locally, even though the weakref itself is
      always static and therefore local.  */
Index: gcc/c-common.c
===================================================================
--- gcc/c-common.c	(.../fsf/trunk)	(revision 6328)
+++ gcc/c-common.c	(.../branches/ifunc)	(revision 6328)
@@ -529,6 +529,7 @@ static tree handle_type_generic_attribut
 static tree handle_alloc_size_attribute (tree *, tree, tree, int, bool *);
 static tree handle_target_attribute (tree *, tree, tree, int, bool *);
 static tree handle_optimize_attribute (tree *, tree, tree, int, bool *);
+static tree handle_ifunc_attribute (tree *, tree, tree, int, bool *);
 
 static void check_function_nonnull (tree, int, tree *);
 static void check_nonnull_arg (void *, tree, unsigned HOST_WIDE_INT);
@@ -817,6 +818,8 @@ const struct attribute_spec c_common_att
 			      handle_target_attribute },
   { "optimize",               1, -1, true, false, false,
 			      handle_optimize_attribute },
+  { "ifunc",                  0, 0, true,  false, false,
+			      handle_ifunc_attribute },
   { NULL,                     0, 0, false, false, false, NULL }
 };
 
@@ -6647,6 +6650,32 @@ handle_weak_attribute (tree *node, tree 
   return NULL_TREE;
 }
 
+/* Handle an "ifunc" attribute; arguments as in
+   struct attribute_spec.handler.  */
+
+static tree
+handle_ifunc_attribute (tree *node, tree name, tree ARG_UNUSED (args),
+			int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  tree decl = *node;
+
+  if (TREE_CODE (decl) == FUNCTION_DECL)
+    {
+      /* This is an IFUNC DECL.  */
+      DECL_IS_IFUNC (decl) = 1;
+      DECL_UNINLINABLE (decl) = 1;
+      TREE_USED (decl) = 1;
+      DECL_PRESERVE_P (decl) = 1;
+    }
+  else
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
 /* Handle an "alias" attribute; arguments as in
    struct attribute_spec.handler.  */
 
Index: gcc/tree-cfg.c
===================================================================
--- gcc/tree-cfg.c	(.../fsf/trunk)	(revision 6328)
+++ gcc/tree-cfg.c	(.../branches/ifunc)	(revision 6328)
@@ -3993,7 +3993,7 @@ static bool
 verify_gimple_return (gimple stmt)
 {
   tree op = gimple_return_retval (stmt);
-  tree restype = TREE_TYPE (TREE_TYPE (cfun->decl));
+  tree restype = function_return_type (cfun->decl);
 
   /* We cannot test for present return values as we do not fix up missing
      return values from the original source.  */
@@ -7265,10 +7265,11 @@ execute_warn_function_return (void)
 
   /* If we see "return;" in some basic block, then we do reach the end
      without returning a value.  */
-  else if (warn_return_type
+  else if ((warn_return_type
+	    || DECL_IS_IFUNC (cfun->decl))
 	   && !TREE_NO_WARNING (cfun->decl)
 	   && EDGE_COUNT (EXIT_BLOCK_PTR->preds) > 0
-	   && !VOID_TYPE_P (TREE_TYPE (TREE_TYPE (cfun->decl))))
+	   && !VOID_TYPE_P (function_return_type (cfun->decl)))
     {
       FOR_EACH_EDGE (e, ei, EXIT_BLOCK_PTR->preds)
 	{
@@ -7280,7 +7281,12 @@ execute_warn_function_return (void)
 	      location = gimple_location (last);
 	      if (location == UNKNOWN_LOCATION)
 		  location = cfun->function_end_locus;
-	      warning_at (location, OPT_Wreturn_type, "control reaches end of non-void function");
+	      if (DECL_IS_IFUNC (cfun->decl))
+		error_at (location,
+			  "control reaches end of indirect function");
+	      else
+		warning_at (location, OPT_Wreturn_type,
+			    "control reaches end of non-void function");
 	      TREE_NO_WARNING (cfun->decl) = 1;
 	      break;
 	    }
Index: gcc/config/elfos.h
===================================================================
--- gcc/config/elfos.h	(.../fsf/trunk)	(revision 6328)
+++ gcc/config/elfos.h	(.../branches/ifunc)	(revision 6328)
@@ -280,7 +280,11 @@ see the files COPYING3 and COPYING.RUNTI
 #define ASM_DECLARE_FUNCTION_NAME(FILE, NAME, DECL)		\
   do								\
     {								\
-      ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "function");	\
+      if (DECL_IS_IFUNC (DECL))					\
+	ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME,			\
+				   "gnu_indirect_function");	\
+      else							\
+	ASM_OUTPUT_TYPE_DIRECTIVE (FILE, NAME, "function");	\
       ASM_DECLARE_RESULT (FILE, DECL_RESULT (DECL));		\
       ASM_OUTPUT_LABEL (FILE, NAME);				\
     }								\
Index: gcc/stmt.c
===================================================================
--- gcc/stmt.c	(.../fsf/trunk)	(revision 6328)
+++ gcc/stmt.c	(.../branches/ifunc)	(revision 6328)
@@ -1559,7 +1559,7 @@ expand_return (tree retval)
   tree retval_rhs;
 
   /* If function wants no value, give it none.  */
-  if (TREE_CODE (TREE_TYPE (TREE_TYPE (current_function_decl))) == VOID_TYPE)
+  if (TREE_CODE (function_return_type (current_function_decl)) == VOID_TYPE)
     {
       expand_normal (retval);
       expand_null_return ();
Index: gcc/ChangeLog.ifunc
===================================================================


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]