PATCH: Add ifunc attribute

H.J. Lu hjl.tools@gmail.com
Sun Jun 21 18:13:00 GMT 2009


On Sun, Jun 21, 2009 at 8:34 AM, Richard
Guenther<richard.guenther@gmail.com> wrote:
> On Sun, Jun 21, 2009 at 4:19 PM, H.J. Lu<hjl.tools@gmail.com> wrote:
>> On Sun, Jun 21, 2009 at 2:23 AM, Jakub Jelinek<jakub@redhat.com> wrote:
>>> On Fri, Jun 19, 2009 at 03:00:04PM -0700, H.J. Lu wrote:
>>>> > Yeah.  I would have proposed that
>>>> >
>>>> > void *foo (void) __attribute__((__ifunc__))
>>>> > {
>>>> >  return zero;
>>>> > }
>>>> >
>>>> > would simply generate foo with indirect function type.
>>>> >
>>>> > This of course causes a problem if you have a declaration for
>>>> > the real foo available - but then you should better not have that,
>>>> > as the compiler will then derive wrong properties for the real foo
>>>> > from the ifunc wrapper body.
>>>> >
>>>> > That would at least not introduce the confusion with the extra name ...
>>>> >
>>>>
>>>> Here is the updated patch. I extended the ifunc attribute to
>>>> static functions and added testcases to show that we need
>>>> argument for ifunc attribute:
>>>
>>> I agree with Richard here, ifunc attribute shouldn't have any arguments
>>> and should just mean the function has gnu_indirect_function type instead
>>> of function, nothing else.  That way users can combine it with other
>>> extensions to get the behavior they want, rather than having weird
>>> unexpected effects (in your case foo_ifunc is for some surprisingly
>>> not defined at all).
>>>
>>> If you don't have foo's prorotype in the current translation unit, you can
>>> just use ifunc attribute alone, if you do have the prototype, it is not
>>> much harder, e.g.
>>>
>>> __typeof (foo) *foo_ifunc (void) __asm ("foo") __attribute__((ifunc))
>>> {
>>>  return foo_impl1;
>>> }
>>>
>>> will work just fine.  In this case you don't need to teach GCC about special
>>> properties of ifunc attribute, foo for GCC isn't defined in the current
>>> translation unit (it doesn't look at DECL_ASSEMBLER_NAME for inlining and
>>> similar purposes).
>>
>> Using
>>
>> static __typeof (foo) *foo_ifunc (void) __asm ("foo") __attribute__((ifunc))
>>
>> is not much better than
>>
>> static __typeof (foo) * foo_ifunc (void) __asm__ ("foo");
>> __asm__(".type foo, %gnu_indirect_function");
>
> Does that work reliably without -fno-toplevel-reorder?  Where is  your
> implementation of foo_ifunc in this case?  A trivial example yields
>
> #APP
>        .type foo, %gnu_indirect_function
> #NO_APP
>        .text
>        .p2align 4,,15
>        .type   foo, @function
> foo:
>        pushl   %ebp
> ...
>
> I wonder if that would be well-defined.

I got

[hjl@gnu-6 gcc]$ cat /tmp/i.c
static int
zero (int x)
{
  return 0;
}

static void * foo_ifunc (void) __asm__ ("foo") __attribute__ ((used));
static void * foo_ifunc (void) { return zero ; }
__asm__(".type foo, %gnu_indirect_function");
[hjl@gnu-6 gcc]$ gcc -O2 -S /tmp/i.c -ftoplevel-reorder
[hjl@gnu-6 gcc]$ cat i.s
	.file	"i.c"
#APP
	.type foo, %gnu_indirect_function
#NO_APP
	.text
	.p2align 4,,15
	.type	zero, @function
zero:
.LFB2:
	xorl	%eax, %eax
	ret
.LFE2:
	.size	zero, .-zero
	.p2align 4,,15
	.type	foo, @function
foo:
.LFB3:
	movl	$zero, %eax
	ret
.LFE3:
	.size	foo, .-foo

I don't see why it should be a problem.

>> Also, I am getting
>>
>> ifunc-2.c:3:12: warning: 'foo' used but never defined
>>
>> on static ifunc.  It is not a new problem:
>>
>> [hjl@gnu-6 tmp]$ cat foo.c
>> static int foo ();
>> static int foo_ifunc (void) __asm__ ("foo");
>> static int
>> foo_ifunc (void)
>> {
>>  return 0;
>> }
>> int
>> bar ()
>> {
>>  return foo ();
>> }
>> [hjl@gnu-6 tmp]$ gcc foo.c -S
>> foo.c:1: warning: âfooâ used but never defined
>> [hjl@gnu-6 tmp]$
>>
>> How can we solve this?
>
> By not declaring foo static.  It would be wrong to do so, as it
> doesn't really bind locally and gcc would not be allowed to
> do optimizations on it that it could do with a locally binding
> function.
>

I added static ifunc support to binutils under request from Jakub.
I am enclosing my current gcc patch, which handles static ifunc
correctly. With static ifunc, you can have 2 ifunc foo in 2 different
files in a library and you don't have to worry if someone else
defines a normal global foo for total different purpose, which
may override your foo.

Thanks.

-- 
H.J.
-------------- next part --------------
gcc/

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

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

	* c-decl.c (c_write_global_declarations_1): Don't warn undefined
	IFUNC symbol.

	* cgraphunit.c (process_function_and_variable_attributes): Check
	ifunc attribute.

	* tree.c (decl_assembler_name): Handle ifunc attribute.

	* tree.h (TREE_IFUNC_NAME): New.

	* varasm.c (default_binds_local_p_1): Return false for
	TREE_IFUNC_NAME.

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

	* doc/extend.texi: Document ifunc attribute.

gcc/testsuite/

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

	* gcc.dg/torture/ifunc-1.c: New.
	* gcc.dg/torture/ifunc-2.c: Likewise.
	* gcc.dg/torture/ifunc-3.c: Likewise.
	* gcc.dg/torture/ifunc-4.c: Likewise.

--- gcc/c-common.c.ifunc	2009-06-18 09:32:41.000000000 -0700
+++ gcc/c-common.c	2009-06-21 07:23:24.000000000 -0700
@@ -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",                  1, 1, true,  false, false,
+			      handle_ifunc_attribute },
   { NULL,                     0, 0, false, false, false, NULL }
 };
 
@@ -6713,6 +6716,62 @@ handle_alias_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 args,
+			int ARG_UNUSED (flags), bool *no_add_attrs)
+{
+  tree decl = *node;
+
+  if (c_language != clk_c)
+    {
+      error ("indirec function %q+D not in C", decl);
+      *no_add_attrs = true;
+    }
+  else if (TREE_CODE (decl) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute ignored", name);
+      *no_add_attrs = true;
+    }
+
+  /* Note that the very first time we process a nested declaration,
+     decl_function_context will not be set.  Indeed, *would* never
+     be set except for the DECL_INITIAL/DECL_EXTERNAL frobbery that
+     we do below.  After such frobbery, pushdecl would set the context.
+     In any case, this is never what we want.  */
+  else if (decl_function_context (decl) == 0
+	   && current_function_decl == NULL)
+    {
+      tree id = TREE_VALUE (args);
+
+      if (TREE_CODE (id) != STRING_CST)
+	{
+	  error ("ifunc argument not a string");
+	  *no_add_attrs = true;
+	  return NULL_TREE;
+	}
+
+      id = get_identifier (TREE_STRING_POINTER (id));
+      /* This counts as a use of the object pointed to.  */
+      TREE_USED (id) = 1;
+      /* This is an IFUNC symbol name.  */
+      TREE_IFUNC_NAME (id) = 1;
+
+      /* Mark it needs to be preserved.  */
+      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 a "weakref" attribute; arguments as in struct
    attribute_spec.handler.  */
 
--- gcc/c-decl.c.ifunc	2009-06-19 11:28:01.000000000 -0700
+++ gcc/c-decl.c	2009-06-21 07:23:24.000000000 -0700
@@ -9315,12 +9315,15 @@ 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.
+	 Don't warn undefined function with IFUNC name since it is
+	 defined by the indirect function.   */
       if (TREE_CODE (decl) == FUNCTION_DECL
 	  && DECL_INITIAL (decl) == 0
 	  && DECL_EXTERNAL (decl)
 	  && !TREE_PUBLIC (decl)
-	  && C_DECL_USED (decl))
+	  && C_DECL_USED (decl)
+	  && !TREE_IFUNC_NAME (DECL_NAME (decl)))
 	{
 	  pedwarn (input_location, 0, "%q+F used but never defined", decl);
 	  TREE_NO_WARNING (decl) = 1;
--- gcc/cgraphunit.c.ifunc	2009-06-18 09:32:41.000000000 -0700
+++ gcc/cgraphunit.c	2009-06-21 07:23:24.000000000 -0700
@@ -836,7 +836,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)
--- gcc/config/elfos.h.ifunc	2009-04-09 09:36:21.000000000 -0700
+++ gcc/config/elfos.h	2009-06-21 07:23:24.000000000 -0700
@@ -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 (TREE_IFUNC_NAME (DECL_ASSEMBLER_NAME (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);				\
     }								\
--- gcc/doc/extend.texi.ifunc	2009-06-18 09:32:28.000000000 -0700
+++ gcc/doc/extend.texi	2009-06-21 07:23:24.000000000 -0700
@@ -1924,6 +1924,28 @@ is not defined in the same translation u
 
 Not all target machines support this attribute.
 
+@item ifunc ("@var{function}")
+@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
+void* f_ifunc () __attribute__ ((ifunc ("f")));
+@end smallexample
+
+defines @samp{f_ifunc} to be an indirect function for @samp{f}.  The
+indirect function @samp{f_ifunc} is called by dynamic linker, and
+returns a pointer to the actual function that should be executed as
+@samp{f}.  This attribute is ignored if @samp{f_ifunc} is undefined
+in the same translation unit unless the symbol type is generated for
+undefined symbol.
+
+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,
--- gcc/testsuite/gcc.dg/torture/ifunc-1.c.ifunc	2009-06-21 07:23:24.000000000 -0700
+++ gcc/testsuite/gcc.dg/torture/ifunc-1.c	2009-06-21 07:23:24.000000000 -0700
@@ -0,0 +1,27 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+
+typedef int (*foo_p) (int);
+
+static int
+zero (int x) 
+{
+  return 0;
+}
+
+foo_p
+__attribute__ ((ifunc ("foo")))
+foo_ifunc (void)
+{ 
+  return zero;
+}
+
+int foo (int);
+
+int
+bar (int i)
+{
+  return foo (i);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+foo_ifunc, .function" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+foo, .gnu_indirect_function" } } */
--- gcc/testsuite/gcc.dg/torture/ifunc-2.c.ifunc	2009-06-21 07:23:24.000000000 -0700
+++ gcc/testsuite/gcc.dg/torture/ifunc-2.c	2009-06-21 07:23:24.000000000 -0700
@@ -0,0 +1,27 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+
+typedef int (*foo_p) (int);
+
+static int
+zero (int x) 
+{
+  return 0;
+}
+
+static foo_p
+__attribute__ ((ifunc ("foo")))
+foo_ifunc (void)
+{ 
+  return zero;
+}
+
+static int foo (int);
+
+int
+bar (int i)
+{
+  return foo (i);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+foo_ifunc, .function" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+foo, .gnu_indirect_function" } } */
--- gcc/testsuite/gcc.dg/torture/ifunc-3.c.ifunc	2009-06-21 07:23:24.000000000 -0700
+++ gcc/testsuite/gcc.dg/torture/ifunc-3.c	2009-06-21 07:23:24.000000000 -0700
@@ -0,0 +1,29 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-fPIE" } */
+
+typedef int (*foo_p) (int);
+
+static int
+zero (int x) 
+{
+  return 0;
+}
+
+static foo_p
+__attribute__ ((ifunc ("foo")))
+foo_ifunc (void)
+{ 
+  return zero;
+}
+
+static int foo (int);
+
+int
+bar (int i)
+{
+  return foo (i);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+foo_ifunc, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+foo@PLT" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+foo, .gnu_indirect_function" } } */
--- gcc/testsuite/gcc.dg/torture/ifunc-4.c.ifunc	2009-06-21 07:23:24.000000000 -0700
+++ gcc/testsuite/gcc.dg/torture/ifunc-4.c	2009-06-21 07:23:24.000000000 -0700
@@ -0,0 +1,29 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-fPIC" } */
+
+typedef int (*foo_p) (int);
+
+static int
+zero (int x) 
+{
+  return 0;
+}
+
+foo_p
+__attribute__ ((ifunc ("foo")))
+foo_ifunc (void)
+{ 
+  return zero;
+}
+
+int foo (int) __attribute__ ((visibility ("hidden")));
+
+int
+bar (int i)
+{
+  return foo (i);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ 	\]\+foo_ifunc, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ 	\]\+foo@PLT" } } */
+/* { dg-final { scan-assembler ".type\[ 	\]\+foo, .gnu_indirect_function" } } */
--- gcc/tree.c.ifunc	2009-06-20 07:13:27.000000000 -0700
+++ gcc/tree.c	2009-06-21 07:23:24.000000000 -0700
@@ -347,7 +347,19 @@ tree
 decl_assembler_name (tree decl)
 {
   if (!DECL_ASSEMBLER_NAME_SET_P (decl))
-    lang_hooks.set_decl_assembler_name (decl);
+    {
+      tree attr;
+
+      if (TREE_CODE (decl) == FUNCTION_DECL
+	  && (attr = lookup_attribute ("ifunc", DECL_ATTRIBUTES (decl))))
+	{
+	  attr = TREE_VALUE (TREE_VALUE (attr));
+	  attr = get_identifier (TREE_STRING_POINTER (attr));
+	  SET_DECL_ASSEMBLER_NAME (decl, attr);
+	}
+      else
+	lang_hooks.set_decl_assembler_name (decl);
+    }
   return DECL_WITH_VIS_CHECK (decl)->decl_with_vis.assembler_name;
 }
 
--- gcc/tree.h.ifunc	2009-06-19 11:42:57.000000000 -0700
+++ gcc/tree.h	2009-06-21 07:23:24.000000000 -0700
@@ -573,6 +573,9 @@ struct GTY(()) tree_common {
        TREE_THIS_NOTRAP in
           (ALIGN/MISALIGNED_)INDIRECT_REF, ARRAY_REF, ARRAY_RANGE_REF
 
+       TREE_IFUNC_NAME in
+           IDENTIFIER_NODE
+
    deprecated_flag:
 
        TREE_DEPRECATED in
@@ -1275,6 +1278,10 @@ extern void omp_clause_range_check_faile
    an exception.  In a CALL_EXPR, nonzero means the call cannot throw.  */
 #define TREE_NOTHROW(NODE) ((NODE)->base.nothrow_flag)
 
+/* In IDENTIFIER_NODEs, nonzero means this is an IFUNC symbol name.  */
+#define TREE_IFUNC_NAME(NODE) \
+  (IDENTIFIER_NODE_CHECK (NODE)->base.nothrow_flag)
+
 /* In a CALL_EXPR, means that it's safe to use the target of the call
    expansion as the return slot for a call that returns in memory.  */
 #define CALL_EXPR_RETURN_SLOT_OPT(NODE) \
--- gcc/varasm.c.ifunc	2009-06-19 11:28:01.000000000 -0700
+++ gcc/varasm.c	2009-06-21 07:23:49.000000000 -0700
@@ -6488,8 +6488,14 @@ 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 (DECL_P (exp)
+      && DECL_NAME (exp)
+      && TREE_IFUNC_NAME (DECL_NAME (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.  */


More information about the Libc-alpha mailing list