gcc/ 2009-06-19 H.J. Lu * 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 * 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. */