diff mbox

IFUNC attribute

Message ID 4C3DDDDC.9050900@codesourcery.com
State New
Headers show

Commit Message

Nathan Sidwell July 14, 2010, 3:55 p.m. UTC
This patch is an update of my previous ifunc patch.  To recap, this adds an 
ifunc attribute that is syntacticly the same as the existing alias attribute. 
The alias attribute names a 'resolver' function, which dynamically determines 
the a function to be called by return a pointer to it.

See http://groups.google.com/group/generic-abi/files for the specification of 
the GNU_IFUNC symbol type and associated relocations.

built and tested on i686-pc-linux-gnu, ok?

nathan

Comments

H.J. Lu July 14, 2010, 3:57 p.m. UTC | #1
On Wed, Jul 14, 2010 at 8:55 AM, Nathan Sidwell <nathan@codesourcery.com> wrote:
> This patch is an update of my previous ifunc patch.  To recap, this adds an
> ifunc attribute that is syntacticly the same as the existing alias
> attribute. The alias attribute names a 'resolver' function, which
> dynamically determines the a function to be called by return a pointer to
> it.
>
> See http://groups.google.com/group/generic-abi/files for the specification
> of the GNU_IFUNC symbol type and associated relocations.
>
> built and tested on i686-pc-linux-gnu, ok?
>

I don't see much value in this than the current scheme. I don't
think it should go in.
Nathan Sidwell July 14, 2010, 4:08 p.m. UTC | #2
On 07/14/10 16:57, H.J. Lu wrote:

> I don't see much value in this than the current scheme. I don't
> think it should go in.

GCC doesn't have a current scheme for handling ifunc functions, I believe you 
have to hack it up with asm inserts -- the same could be done to provide the 
functionality of the alias attribute, and yet it is in GCC.

nathan
H.J. Lu July 14, 2010, 4:16 p.m. UTC | #3
On Wed, Jul 14, 2010 at 9:08 AM, Nathan Sidwell <nathan@codesourcery.com> wrote:
> On 07/14/10 16:57, H.J. Lu wrote:
>
>> I don't see much value in this than the current scheme. I don't
>> think it should go in.
>
> GCC doesn't have a current scheme for handling ifunc functions, I believe
> you have to hack it up with asm inserts -- the same could be done to provide
> the functionality of the alias attribute, and yet it is in GCC.
>

As I said before, the current asm statement scheme works to
certain degree. As far as user is concerned, your scheme is
very similar and has the same drawbacks.
Michael Matz July 20, 2010, 1:09 p.m. UTC | #4
Hi,

On Wed, 14 Jul 2010, H.J. Lu wrote:

> On Wed, Jul 14, 2010 at 9:08 AM, Nathan Sidwell <nathan@codesourcery.com> wrote:
> > On 07/14/10 16:57, H.J. Lu wrote:
> >
> >> I don't see much value in this than the current scheme. I don't
> >> think it should go in.
> >
> > GCC doesn't have a current scheme for handling ifunc functions, I believe
> > you have to hack it up with asm inserts -- the same could be done to provide
> > the functionality of the alias attribute, and yet it is in GCC.
> 
> As I said before, the current asm statement scheme works to
> certain degree. As far as user is concerned, your scheme is
> very similar and has the same drawbacks.

toplevel asms that fiddle with symbols have one huge drawback: they don't 
work well with LTO.  As in, the semantics of the asms are not known to the 
compiler, for instance that creating an alias via asm, and using that 
alias in C code make the aliased function necessary.  Same with ifunc, the 
resolver function would magically need to become necessary (e.g. via an 
explicit 'used' attribute which has drawbacks too) with just asms, whereas 
with an attribute it's obvious to the cgraph code.

It's really much better to be explicit to the compiler, hence attributes.


Ciao,
Michael.
Nathan Sidwell Aug. 31, 2010, 12:21 p.m. UTC | #5
Hi,
can someone review my ifunc attribute patch at
http://gcc.gnu.org/ml/gcc-patches/2010-07/msg01165.html

> This patch is an update of my previous ifunc patch.  To recap, this adds
> an ifunc attribute that is syntacticly the same as the existing alias
> attribute. The alias attribute names a 'resolver' function, which
> dynamically determines the a function to be called by return a pointer
> to it.
>
> See http://groups.google.com/group/generic-abi/files for the
> specification of the GNU_IFUNC symbol type and associated relocations.
>
> built and tested on i686-pc-linux-gnu, ok?

thanks.

nathan
Mark Mitchell Sept. 8, 2010, 5:41 p.m. UTC | #6
On 8/31/2010 5:21 AM, Nathan Sidwell wrote:

> can someone review my ifunc attribute patch at
> http://gcc.gnu.org/ml/gcc-patches/2010-07/msg01165.html

OK, with a minor nit: handle_{ifunc,alias}_attribute need comments.

Thanks,
diff mbox

Patch

2010-07-14  Nathan Sidwell  <nathan@codesourcery.com>

	* configure.ac (gnu_indirect_function): New test.
	* configure: Rebuilt.
	* config.in (HAVE_GAS_INDIRECT_FUNCTION): New.
	* defaults.h (IFUNC_ASM_TYPE): Provide default.

	* doc/extend.texi (Function Attributes): Document ifunc.
	* varasm.c (do_assemble_alias): Deal with ifuncs too.

	c-family/
	* c-common.c (handle_alias_ifunc_attribute): New, broken out of	...
	(handle_alias_attribute): ... here.
	(handle_ifunc_attribute): New.

	testsuite/
	* lib/target-supports-dg.exp (dg-require-ifunc): New.
	* lib/target-supports.exp (check_ifunc_available): New.
	* gcc.dg/attr-ifunc-1.c: New.
	* gcc.dg/attr-ifunc-2.c: New.
	* gcc.dg/attr-ifunc-3.c: New.
	* gcc.dg/attr-ifunc-4.c: New.
	* gcc.dg/attr-ifunc-5.c: New.
	* testsuite/g++.dg/ext/attr-ifunc-1.C
	* testsuite/g++.dg/ext/attr-ifunc-2.C
	* testsuite/g++.dg/ext/attr-ifunc-3.C
	* testsuite/g++.dg/ext/attr-ifunc-4.C

Index: doc/extend.texi
===================================================================
--- doc/extend.texi	(revision 162137)
+++ doc/extend.texi	(working copy)
@@ -1911,6 +1911,7 @@ 
 @cindex functions that do not pop the argument stack on the 386
 @cindex functions that have different compilation options on the 386
 @cindex functions that have different optimization options
+@cindex functions that are dynamically resolved
 
 In GNU C, you declare certain things about functions called in your program
 which help the compiler optimize function calls and check your code more
@@ -1926,13 +1927,13 @@ 
 @code{nothrow}, @code{sentinel}, @code{format}, @code{format_arg},
 @code{no_instrument_function}, @code{section}, @code{constructor},
 @code{destructor}, @code{used}, @code{unused}, @code{deprecated},
-@code{weak}, @code{malloc}, @code{alias}, @code{warn_unused_result},
-@code{nonnull}, @code{gnu_inline}, @code{externally_visible},
-@code{hot}, @code{cold}, @code{artificial}, @code{error} and
-@code{warning}.  Several other attributes are defined for functions on
-particular target systems.  Other attributes, including @code{section}
-are supported for variables declarations (@pxref{Variable Attributes})
-and for types (@pxref{Type Attributes}).
+@code{weak}, @code{malloc}, @code{alias}, @code{ifunc},
+@code{warn_unused_result}, @code{nonnull}, @code{gnu_inline},
+@code{externally_visible}, @code{hot}, @code{cold}, @code{artificial},
+@code{error} and @code{warning}.  Several other attributes are defined
+for functions on particular target systems.  Other attributes,
+including @code{section} are supported for variables declarations
+(@pxref{Variable Attributes}) and for types (@pxref{Type Attributes}).
 
 GCC plugins may provide their own attributes.
 
@@ -2584,6 +2585,51 @@ 
                      use_debug_exception_return)) v7 ();
 @end smallexample
 
+@item ifunc ("@var{resolver}")
+@cindex @code{ifunc} attribute
+The @code{ifunc} attribute is used to mark a function as an indirect
+function using the STT_GNU_IFUNC symbol type extension to the ELF
+standard.  This allows the resolution of the symbol value to be
+determined dynamically at load time, and an optimized version of the
+routine can be selected for the particular processor or other system
+characteristics determined then.  To use this attribute, first define
+the implementation functions available, and a resolver function that
+returns a pointer to the selected implementation function.  The
+implementation functions' declarations must match the API of the
+function being implemented, the resolver's declaration is be a
+function returning pointer to void function returning void:
+
+@smallexample
+void *my_memcpy (void *dst, const void *src, size_t len)
+@{
+  @dots{}
+@}
+
+static void (*resolve_memcpy (void)) (void)
+@{
+  return my_memcpy; // we'll just always select this routine
+@}
+@end smallexample
+
+The exported header file declaring the function the user calls would
+contain:
+
+@smallexample
+extern void *memcpy (void *, const void *, size_t);
+@end smallexample
+
+allowing the user to call this as a regular function, unaware of the
+implementation.  Finally, the indirect function needs to be defined in
+the same translation unit as the resolver function:
+
+@smallexample
+void *memcpy (void *, const void *, size_t)
+     __attribute__ ((ifunc ("resolve_memcpy")));
+@end smallexample
+
+Indirect functions cannot be weak, and require a recent binutils (at
+least version 2.20.1), and GNU C library (at least version 2.11.1).
+
 @item interrupt_handler
 @cindex interrupt handler functions on the Blackfin, m68k, H8/300 and SH processors
 Use this attribute on the Blackfin, m68k, H8/300, H8/300H, H8S, and SH to
Index: c-family/c-common.c
===================================================================
--- c-family/c-common.c	(revision 162137)
+++ c-family/c-common.c	(working copy)
@@ -327,6 +327,8 @@ 
 static tree handle_section_attribute (tree *, tree, tree, int, bool *);
 static tree handle_aligned_attribute (tree *, tree, tree, int, bool *);
 static tree handle_weak_attribute (tree *, tree, tree, int, bool *) ;
+static tree handle_alias_ifunc_attribute (bool, tree *, tree, tree, bool *);
+static tree handle_ifunc_attribute (tree *, tree, tree, int, bool *);
 static tree handle_alias_attribute (tree *, tree, tree, int, bool *);
 static tree handle_weakref_attribute (tree *, tree, tree, int, bool *) ;
 static tree handle_visibility_attribute (tree *, tree, tree, int,
@@ -599,6 +601,8 @@ 
 			      handle_aligned_attribute },
   { "weak",                   0, 0, true,  false, false,
 			      handle_weak_attribute },
+  { "ifunc",                  1, 1, true,  false, false,
+			      handle_ifunc_attribute },
   { "alias",                  1, 1, true,  false, false,
 			      handle_alias_attribute },
   { "weakref",                0, 1, true,  false, false,
@@ -6626,6 +6630,12 @@ 
       error ("inline function %q+D cannot be declared weak", *node);
       *no_add_attrs = true;
     }
+  else if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (*node)))
+    {
+      error ("indirect function %q+D cannot be declared weak", *node);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
   else if (TREE_CODE (*node) == FUNCTION_DECL
 	   || TREE_CODE (*node) == VAR_DECL)
     declare_weak (*node);
@@ -6635,16 +6645,17 @@ 
   return NULL_TREE;
 }
 
-/* Handle an "alias" attribute; arguments as in
+/* Handle an "alias" or "ifunc" attribute; arguments as in
    struct attribute_spec.handler.  */
 
 static tree
-handle_alias_attribute (tree *node, tree name, tree args,
-			int ARG_UNUSED (flags), bool *no_add_attrs)
+handle_alias_ifunc_attribute (bool is_alias, tree *node, tree name, tree args,
+			      bool *no_add_attrs)
 {
   tree decl = *node;
 
-  if (TREE_CODE (decl) != FUNCTION_DECL && TREE_CODE (decl) != VAR_DECL)
+  if (TREE_CODE (decl) != FUNCTION_DECL
+      && (!is_alias || TREE_CODE (decl) != VAR_DECL))
     {
       warning (OPT_Wattributes, "%qE attribute ignored", name);
       *no_add_attrs = true;
@@ -6657,9 +6668,18 @@ 
       || (TREE_CODE (decl) != FUNCTION_DECL
 	  && ! TREE_PUBLIC (decl) && DECL_INITIAL (decl)))
     {
-      error ("%q+D defined both normally and as an alias", decl);
+      error ("%q+D defined both normally and as %qE attribute", decl, name);
       *no_add_attrs = true;
+      return NULL_TREE;
     }
+  else if (!is_alias
+	   && (lookup_attribute ("weak", DECL_ATTRIBUTES (decl)) 
+	       || lookup_attribute ("weakref", DECL_ATTRIBUTES (decl))))
+    {
+      error ("weak %q+D cannot be defined %qE", decl, name);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }			 
 
   /* Note that the very first time we process a nested declaration,
      decl_function_context will not be set.  Indeed, *would* never
@@ -6673,7 +6693,7 @@ 
       id = TREE_VALUE (args);
       if (TREE_CODE (id) != STRING_CST)
 	{
-	  error ("alias argument not a string");
+	  error ("attribute %qE argument not a string", name);
 	  *no_add_attrs = true;
 	  return NULL_TREE;
 	}
@@ -6691,6 +6711,11 @@ 
 	    DECL_EXTERNAL (decl) = 0;
 	  TREE_STATIC (decl) = 1;
 	}
+
+      if (!is_alias)
+	/* ifuncs are also aliases, so set that attribute too. */
+	DECL_ATTRIBUTES (decl)
+	  = tree_cons (get_identifier ("alias"), args, DECL_ATTRIBUTES (decl));
     }
   else
     {
@@ -6701,6 +6726,20 @@ 
   return NULL_TREE;
 }
 
+static tree
+handle_ifunc_attribute (tree *node, tree name, tree args,
+			int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  return handle_alias_ifunc_attribute (false, node, name, args, no_add_attrs);
+}
+
+static tree
+handle_alias_attribute (tree *node, tree name, tree args,
+			int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  return handle_alias_ifunc_attribute (true, node, name, args, no_add_attrs);
+}
+
 /* Handle a "weakref" attribute; arguments as in struct
    attribute_spec.handler.  */
 
@@ -6722,6 +6761,13 @@ 
       return NULL_TREE;
     }
 
+  if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (*node)))
+    {
+      error ("indirect function %q+D cannot be declared weakref", *node);
+      *no_add_attrs = true;
+      return NULL_TREE;
+    }
+
   /* The idea here is that `weakref("name")' mutates into `weakref,
      alias("name")', and weakref without arguments, in turn,
      implicitly adds weak. */
Index: defaults.h
===================================================================
--- defaults.h	(revision 162137)
+++ defaults.h	(working copy)
@@ -110,6 +110,10 @@ 
 #endif
 #endif
 
+#ifndef IFUNC_ASM_TYPE
+#define IFUNC_ASM_TYPE "gnu_indirect_function"
+#endif
+
 #ifndef TLS_COMMON_ASM_OP
 #define TLS_COMMON_ASM_OP ".tls_common"
 #endif
Index: configure
===================================================================
--- configure	(revision 162137)
+++ configure	(working copy)
@@ -21297,6 +21297,48 @@ 
 $as_echo "$gcc_cv_as_hidden" >&6; }
 
 
+# gnu_indirect_function type is an extension proposed at
+# http://groups.google/com/group/generic-abi/files. It allows dynamic runtime
+# selection of function implementation
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking assembler for gnu_indirect_function" >&5
+$as_echo_n "checking assembler for gnu_indirect_function... " >&6; }
+if test "${gcc_cv_as_indirect_function+set}" = set; then :
+  $as_echo_n "(cached) " >&6
+else
+  gcc_cv_as_indirect_function=no
+    if test $in_tree_gas = yes; then
+    if test $in_tree_gas_is_elf = yes \
+  && test $gcc_cv_gas_vers -ge `expr \( \( 2 \* 1000 \) + 20 \) \* 1000 + 1`
+  then gcc_cv_as_indirect_function=yes
+fi
+  elif test x$gcc_cv_as != x; then
+    echo '	.type  Foo, @gnu_indirect_function
+Foo:' > conftest.s
+    if { ac_try='$gcc_cv_as $gcc_cv_as_flags  -o conftest.o conftest.s >&5'
+  { { eval echo "\"\$as_me\":${as_lineno-$LINENO}: \"$ac_try\""; } >&5
+  (eval $ac_try) 2>&5
+  ac_status=$?
+  $as_echo "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5
+  test $ac_status = 0; }; }
+    then
+	gcc_cv_as_indirect_function=yes
+    else
+      echo "configure: failed program was" >&5
+      cat conftest.s >&5
+    fi
+    rm -f conftest.o conftest.s
+  fi
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $gcc_cv_as_indirect_function" >&5
+$as_echo "$gcc_cv_as_indirect_function" >&6; }
+
+
+if test $gcc_cv_as_indirect_function = yes ; then
+
+$as_echo "#define HAVE_GAS_INDIRECT_FUNCTION 1" >>confdefs.h
+
+fi
+
 if test $in_tree_ld != yes ; then
   ld_ver=`$gcc_cv_ld --version 2>/dev/null | sed 1q`
   if test x"$ld_is_gold" = xyes; then
Index: testsuite/gcc.dg/attr-ifunc-1.c
===================================================================
--- testsuite/gcc.dg/attr-ifunc-1.c	(revision 0)
+++ testsuite/gcc.dg/attr-ifunc-1.c	(revision 0)
@@ -0,0 +1,23 @@ 
+/* { dg-do run }  */
+/* { dg-require-ifunc "" } */
+/* { dg-options "" } */
+
+#include <stdio.h>
+
+static int implementation (void)
+{
+  printf ("'ere I am JH\n");
+  return 0;
+}
+
+static void *resolver (void)
+{
+  return (void *)implementation;
+}
+
+extern int magic (void) __attribute__ ((ifunc ("resolver")));
+
+int main ()
+{
+  return magic () != 0;
+}
Index: testsuite/gcc.dg/attr-ifunc-5.c
===================================================================
--- testsuite/gcc.dg/attr-ifunc-5.c	(revision 0)
+++ testsuite/gcc.dg/attr-ifunc-5.c	(revision 0)
@@ -0,0 +1,23 @@ 
+/* { dg-do run }  */
+/* { dg-require-ifunc "" } */
+/* { dg-options "" } */
+
+#include <stdio.h>
+
+static void *implementation (void)
+{
+  printf ("'ere I am JH\n");
+  return 0;
+}
+
+static void *resolver (void)
+{
+  return (void *)implementation;
+}
+
+extern int magic (void) __attribute__ ((ifunc ("resolver"),visibility ("hidden")));
+
+int main ()
+{
+  return magic () != 0;
+}
Index: testsuite/gcc.dg/attr-ifunc-2.c
===================================================================
--- testsuite/gcc.dg/attr-ifunc-2.c	(revision 0)
+++ testsuite/gcc.dg/attr-ifunc-2.c	(revision 0)
@@ -0,0 +1,28 @@ 
+/* { dg-require-ifunc "" } */
+
+static void *resolver ()
+{
+  return 0;
+}
+
+extern int magic (void)  /* { dg-message "previous definition" } */
+     __attribute__ ((ifunc ("resolver")));
+extern int magic (void)  /* { dg-error "redefinition" "" } */
+     __attribute__ ((alias ("resolver")));
+
+extern int spell (void)  /* { dg-message "previous definition" } */
+{
+  return 0;
+}
+extern int spell (void)  /* { dg-error "redefinition" "" } */
+     __attribute__ ((ifunc ("resolver")));
+
+extern int mantra (void)  /* { dg-message "previous definition" } */
+     __attribute__ ((alias ("resolver")));
+extern int mantra (void)  /* { dg-error "redefinition" "" } */
+     __attribute__ ((ifunc ("resolver")));
+
+extern int saying (void)  /* { dg-error "weak .* cannot be defined" "" } */
+     __attribute__ ((weak,ifunc ("resolver")));
+extern int maxim (void) /* { dg-error "indirect function .* cannot be declared weak" "" } */
+     __attribute__ ((ifunc ("resolver"),weak));
Index: testsuite/gcc.dg/attr-ifunc-3.c
===================================================================
--- testsuite/gcc.dg/attr-ifunc-3.c	(revision 0)
+++ testsuite/gcc.dg/attr-ifunc-3.c	(revision 0)
@@ -0,0 +1,27 @@ 
+/* { dg-do run }  */
+/* { dg-require-ifunc "" } */
+/* { dg-options "" } */
+
+#include <stdio.h>
+
+static int __attribute__((noinline))
+     implementation (void *ptr)
+{
+  if (ptr)
+    return ((int (*) (void *))ptr) (0);
+  
+  printf ("'ere I am JH\n");
+  return 0;
+}
+
+static void *resolver (void)
+{
+  return (void *)implementation;
+}
+
+extern int magic (void *) __attribute__ ((ifunc ("resolver")));
+
+int main ()
+{
+  return magic ((void *)magic);
+}
Index: testsuite/gcc.dg/attr-ifunc-4.c
===================================================================
--- testsuite/gcc.dg/attr-ifunc-4.c	(revision 0)
+++ testsuite/gcc.dg/attr-ifunc-4.c	(revision 0)
@@ -0,0 +1,23 @@ 
+/* { dg-do run }  */
+/* { dg-require-ifunc "" } */
+/* { dg-options "" } */
+
+#include <stdio.h>
+
+static void *implementation (void)
+{
+  printf ("'ere I am JH\n");
+  return 0;
+}
+
+static void *resolver (void)
+{
+  return (void *)implementation;
+}
+
+static int magic (void) __attribute__ ((ifunc ("resolver")));
+
+int main ()
+{
+  return magic () != 0;
+}
Index: testsuite/g++.dg/ext/attr-ifunc-1.C
===================================================================
--- testsuite/g++.dg/ext/attr-ifunc-1.C	(revision 0)
+++ testsuite/g++.dg/ext/attr-ifunc-1.C	(revision 0)
@@ -0,0 +1,34 @@ 
+/* { dg-do run }  */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-Wno-pmf-conversions" } */
+
+#include <stdio.h>
+
+struct Klass
+{
+  int implementation ();
+  int magic ();
+  static void *resolver ();
+};
+
+int Klass::implementation (void)
+{
+  printf ("'ere I am JH\n");
+  return 0;
+}
+
+void *Klass::resolver (void)
+{
+  int (Klass::*pmf) () = &Klass::implementation;
+  
+  return (void *)(int (*)(Klass *))(((Klass *)0)->*pmf);
+}
+
+int Klass::magic (void) __attribute__ ((ifunc ("_ZN5Klass8resolverEv")));
+
+int main ()
+{
+  Klass obj;
+  
+  return obj.magic () != 0;
+}
Index: testsuite/g++.dg/ext/attr-ifunc-2.C
===================================================================
--- testsuite/g++.dg/ext/attr-ifunc-2.C	(revision 0)
+++ testsuite/g++.dg/ext/attr-ifunc-2.C	(revision 0)
@@ -0,0 +1,38 @@ 
+/* { dg-do run }  */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-Wno-pmf-conversions" } */
+
+#include <stdio.h>
+
+struct Klass
+{
+  int implementation ();
+  int magic ();
+  static void *resolver ();
+};
+
+int Klass::implementation (void)
+{
+  printf ("'ere I am JH\n");
+  return 0;
+}
+
+void *Klass::resolver (void)
+{
+  int (Klass::*pmf) () = &Klass::implementation;
+  
+  return (void *)(int (*)(Klass *))(((Klass *)0)->*pmf);
+}
+
+int Klass::magic (void) __attribute__ ((ifunc ("_ZN5Klass8resolverEv")));
+
+struct Klassier : Klass
+{
+};
+
+int main ()
+{
+  Klassier obj;
+  
+  return obj.magic () != 0;
+}
Index: testsuite/g++.dg/ext/attr-ifunc-3.C
===================================================================
--- testsuite/g++.dg/ext/attr-ifunc-3.C	(revision 0)
+++ testsuite/g++.dg/ext/attr-ifunc-3.C	(revision 0)
@@ -0,0 +1,39 @@ 
+/* { dg-do run }  */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-Wno-pmf-conversions" } */
+
+#include <stdio.h>
+
+struct Klass
+{
+  int implementation ();
+  int magic ();
+  static void *resolver ();
+};
+
+int Klass::implementation (void)
+{
+  printf ("'ere I am JH\n");
+  return 0;
+}
+
+void *Klass::resolver (void)
+{
+  int (Klass::*pmf) () = &Klass::implementation;
+  
+  return (void *)(int (*)(Klass *))(((Klass *)0)->*pmf);
+}
+
+int Klass::magic (void) __attribute__ ((ifunc ("_ZN5Klass8resolverEv")));
+
+int Foo (Klass &obj, int (Klass::*pmf) ())
+{
+  return (obj.*pmf) ();
+}
+
+int main ()
+{
+  Klass obj;
+  
+  return Foo (obj, &Klass::magic) != 0;
+}
Index: testsuite/g++.dg/ext/attr-ifunc-4.C
===================================================================
--- testsuite/g++.dg/ext/attr-ifunc-4.C	(revision 0)
+++ testsuite/g++.dg/ext/attr-ifunc-4.C	(revision 0)
@@ -0,0 +1,44 @@ 
+/* { dg-do run }  */
+/* { dg-require-ifunc "" } */
+/* { dg-options "-Wno-pmf-conversions" } */
+
+#include <stdio.h>
+
+struct Klass
+{
+  virtual int magic () = 0;
+};
+
+struct Klassier : Klass
+{
+  int implementation ();
+  int magic ();
+  static void *resolver ();
+};
+
+int Klassier::implementation (void)
+{
+  printf ("'ere I am JH\n");
+  return 0;
+}
+
+void *Klassier::resolver (void)
+{
+  int (Klassier::*pmf) () = &Klassier::implementation;
+  
+  return (void *)(int (*)(Klassier *))(((Klassier *)0)->*pmf);
+}
+
+int Klassier::magic (void) __attribute__ ((ifunc ("_ZN8Klassier8resolverEv")));
+
+int __attribute__ ((weak)) Foo (Klass &base)
+{
+  return base.magic ();
+}
+
+int main ()
+{
+  Klassier obj;
+  
+  return Foo (obj) != 0;
+}
Index: testsuite/lib/target-supports-dg.exp
===================================================================
--- testsuite/lib/target-supports-dg.exp	(revision 162137)
+++ testsuite/lib/target-supports-dg.exp	(working copy)
@@ -90,6 +90,21 @@ 
     }
 }
 
+# If this target does not support the "ifunc" attribute, skip this
+# test.
+
+proc dg-require-ifunc { args } {
+    set ifunc_available [ check_ifunc_available ]
+    if { $ifunc_available == -1 } {
+	upvar name name
+	unresolved "$name"
+    }
+    if { $ifunc_available < 2 } {
+	upvar dg-do-what dg-do-what
+	set dg-do-what [list [lindex ${dg-do-what} 0] "N" "P"]
+    }
+}
+
 # If this target's linker does not support the --gc-sections flag,
 # skip this test.
 
Index: testsuite/lib/target-supports.exp
===================================================================
--- testsuite/lib/target-supports.exp	(revision 162137)
+++ testsuite/lib/target-supports.exp	(working copy)
@@ -368,6 +368,56 @@ 
     return $alias_available_saved
 }
 
+###############################
+# proc check_ifunc_available { }
+###############################
+
+# Determine if the target toolchain supports the alias attribute.
+
+# Returns 2 if the target supports aliases.  Returns 1 if the target
+# only supports weak aliased.  Returns 0 if the target does not
+# support aliases at all.  Returns -1 if support for aliases could not
+# be determined.
+
+proc check_ifunc_available { } {
+    global ifunc_available_saved
+    global tool
+
+    if [info exists ifunc_available_saved] {
+        verbose "check_ifunc_available  returning saved $ifunc_available_saved" 2
+    } else {
+	set src ifunc[pid].c
+	set obj ifunc[pid].o
+        verbose "check_ifunc_available  compiling testfile $src" 2
+	set f [open $src "w"]
+	# Compile a small test program.  The definition of "g" is
+	# necessary to keep the Solaris assembler from complaining
+	# about the program.
+	puts $f "#ifdef __cplusplus\nextern \"C\"\n#endif\n"
+	puts $f "void g() {} void f() __attribute__((ifunc(\"g\")));"
+	close $f
+	set lines [${tool}_target_compile $src $obj object ""]
+	file delete $src
+	remote_file build delete $obj
+
+	if [string match "" $lines] then {
+	    # No error messages, everything is OK.
+	    set ifunc_available_saved 2
+	} else {
+	    if [regexp "ifunc is not supported" $lines] {
+		verbose "check_ifunc_available  target does not support ifunc" 2
+		set ifunc_available_saved 0
+	    } else {
+		set ifunc_available_saved -1
+	    }
+	}
+
+	verbose "check_ifunc_available  returning $ifunc_available_saved" 2
+    }
+
+    return $ifunc_available_saved
+}
+
 # Returns true if --gc-sections is supported on the target.
 
 proc check_gc_sections_available { } {
Index: config.in
===================================================================
--- config.in	(revision 162137)
+++ config.in	(working copy)
@@ -968,6 +968,9 @@ 
 /* Define if your assembler and linker support .hidden. */
 #undef HAVE_GAS_HIDDEN
 
+/* Define if your assembler supports indirect function type. */
+#undef HAVE_GAS_INDIRECT_FUNCTION
+
 /* Define if your assembler supports .lcomm with an alignment field. */
 #ifndef USED_FOR_TARGET
 #undef HAVE_GAS_LCOMM_WITH_ALIGNMENT
Index: configure.ac
===================================================================
--- configure.ac	(revision 162137)
+++ configure.ac	(working copy)
@@ -2182,6 +2182,19 @@ 
     ;;
 esac])
 
+# gnu_indirect_function type is an extension proposed at
+# http://groups.google/com/group/generic-abi/files. It allows dynamic runtime
+# selection of function implementation
+gcc_GAS_CHECK_FEATURE(gnu_indirect_function, gcc_cv_as_indirect_function,
+ [elf,2,20,1],,
+[	.type  Foo, @gnu_indirect_function
+Foo:])
+GCC_TARGET_TEMPLATE([HAVE_GAS_INDIRECT_FUNCTION])
+if test $gcc_cv_as_indirect_function = yes ; then
+  AC_DEFINE(HAVE_GAS_INDIRECT_FUNCTION, 1,
+  [Define if your assembler supports indirect function type.])
+fi
+
 changequote(,)dnl
 if test $in_tree_ld != yes ; then
   ld_ver=`$gcc_cv_ld --version 2>/dev/null | sed 1q`
Index: varasm.c
===================================================================
--- varasm.c	(revision 162137)
+++ varasm.c	(working copy)
@@ -1312,7 +1312,8 @@ 
   if (DECL_INITIAL (decl) == decl)
     return false;
 
-  /* If this decl is an alias, then we don't want to emit a definition.  */
+  /* If this decl is an alias, then we don't want to emit a
+     definition.  */
   if (lookup_attribute ("alias", DECL_ATTRIBUTES (decl)))
     return false;
 
@@ -5747,6 +5748,17 @@ 
       globalize_decl (decl);
       maybe_assemble_visibility (decl);
     }
+  if (lookup_attribute ("ifunc", DECL_ATTRIBUTES (decl)))
+    {
+#if defined (ASM_OUTPUT_TYPE_DIRECTIVE) && HAVE_GAS_INDIRECT_FUNCTION
+      ASM_OUTPUT_TYPE_DIRECTIVE
+	(asm_out_file, IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl)),
+	 IFUNC_ASM_TYPE);
+#else
+      error_at (DECL_SOURCE_LOCATION (decl),
+		"ifunc is not supported in this configuration");
+#endif
+    }
 
 # ifdef ASM_OUTPUT_DEF_FROM_DECLS
   ASM_OUTPUT_DEF_FROM_DECLS (asm_out_file, decl, target);