summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gcc/ChangeLog.ifunc55
-rw-r--r--gcc/c-decl.c71
-rw-r--r--gcc/c-family/ChangeLog.ifunc5
-rw-r--r--gcc/c-family/c-common.c29
-rw-r--r--gcc/c-typeck.c2
-rw-r--r--gcc/cgraphunit.c18
-rw-r--r--gcc/config/elfos.h6
-rw-r--r--gcc/cp/ChangeLog.ifunc29
-rw-r--r--gcc/cp/call.c7
-rw-r--r--gcc/cp/class.c19
-rw-r--r--gcc/cp/cp-tree.h6
-rw-r--r--gcc/cp/decl.c79
-rw-r--r--gcc/cp/typeck.c34
-rw-r--r--gcc/doc/extend.texi24
-rw-r--r--gcc/gimplify.c2
-rw-r--r--gcc/ipa-pure-const.c14
-rw-r--r--gcc/stmt.c2
-rw-r--r--gcc/testsuite/ChangeLog.ifunc71
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-1.C25
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-10.C9
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-11.C9
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-12.C9
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-13.C9
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-14.C10
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-15.C46
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-16.C48
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-17.C48
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-18.C25
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-19.C15
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-2.C25
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-20.C27
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-21.C46
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-22.C48
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-23.C48
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-24.C49
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-25.C51
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-26.C51
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-27.C63
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-28.C63
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-29.C59
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-3.C25
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-30.C59
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-4.C25
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-5.C24
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-6.C5
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-7.C5
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-8.C25
-rw-r--r--gcc/testsuite/g++.dg/torture/ifunc-9.C20
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-1.c25
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-10.c9
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-11.c9
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-12.c9
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-13.c9
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-14.c10
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-15.c27
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-16.c27
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-17.c13
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-18.c27
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-2.c25
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-3.c25
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-4.c25
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-5.c24
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-6.c5
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-7.c5
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-8.c16
-rw-r--r--gcc/testsuite/gcc.dg/torture/ifunc-9.c20
-rw-r--r--gcc/tree-cfg.c14
-rw-r--r--gcc/tree.h19
-rw-r--r--gcc/varasm.c7
69 files changed, 1738 insertions, 56 deletions
diff --git a/gcc/ChangeLog.ifunc b/gcc/ChangeLog.ifunc
new file mode 100644
index 00000000000..31e7470074a
--- /dev/null
+++ b/gcc/ChangeLog.ifunc
@@ -0,0 +1,55 @@
+2009-07-06 H.J. Lu <hongjiu.lu@intel.com>
+
+ * ipa-pure-const.c (check_stmt): Revert the last change.
+ (analyze_function): Skip analysis for indirect function.
+
+2009-07-06 H.J. Lu <hongjiu.lu@intel.com>
+
+ * ipa-pure-const.c (check_stmt): Properly handle indirect function
+ return.
+
+2009-06-28 H.J. Lu <hongjiu.lu@intel.com>
+
+ * doc/extend.texi: Update ifunc attribute document.
+
+2009-06-28 H.J. Lu <hongjiu.lu@intel.com>
+
+ PR c/40528
+ * 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.
+ (function_return_type): Likewise.
+
+ * 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.
diff --git a/gcc/c-decl.c b/gcc/c-decl.c
index 010421c0b89..f95a79e0aa5 100644
--- a/gcc/c-decl.c
+++ b/gcc/c-decl.c
@@ -2408,6 +2408,23 @@ merge_decls (tree newdecl, tree olddecl, tree newtype, tree oldtype)
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);
@@ -7625,7 +7642,7 @@ start_function (struct c_declspecs *declspecs, struct c_declarator *declarator,
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;
@@ -7678,7 +7695,12 @@ store_parm_decls_newstyle (tree fndecl, const struct c_arg_info *arg_info)
warn_if_shadowing (decl);
}
else
- error_at (DECL_SOURCE_LOCATION (decl), "parameter name omitted");
+ {
+ /* Parameters in IFUNC function are never used. */
+ if (!DECL_IS_IFUNC (current_function_decl))
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "parameter name omitted");
+ }
}
/* Record the parameter list in the function declaration. */
@@ -8104,8 +8126,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
@@ -8119,8 +8142,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;
}
@@ -9561,7 +9587,7 @@ finish_declspecs (struct c_declspecs *specs)
GLOBALS. */
static void
-c_write_global_declarations_1 (tree globals)
+c_write_global_declarations_1 (tree globals, bool ext_scope)
{
tree decl;
bool reconsider;
@@ -9571,15 +9597,30 @@ c_write_global_declarations_1 (tree globals)
{
/* 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);
@@ -9690,8 +9731,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 = DECL_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. */
diff --git a/gcc/c-family/ChangeLog.ifunc b/gcc/c-family/ChangeLog.ifunc
new file mode 100644
index 00000000000..327cad4806c
--- /dev/null
+++ b/gcc/c-family/ChangeLog.ifunc
@@ -0,0 +1,5 @@
+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".
diff --git a/gcc/c-family/c-common.c b/gcc/c-family/c-common.c
index e2a0561c7c6..3614d0e8e9e 100644
--- a/gcc/c-family/c-common.c
+++ b/gcc/c-family/c-common.c
@@ -356,6 +356,7 @@ 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_fnspec_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);
@@ -658,6 +659,8 @@ const struct attribute_spec c_common_attribute_table[] =
The name contains space to prevent its usage in source code. */
{ "fn spec", 1, 1, false, true, true,
handle_fnspec_attribute },
+ { "ifunc", 0, 0, true, false, false,
+ handle_ifunc_attribute },
{ NULL, 0, 0, false, false, false, NULL }
};
@@ -6635,6 +6638,32 @@ handle_weak_attribute (tree *node, tree name,
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. */
diff --git a/gcc/c-typeck.c b/gcc/c-typeck.c
index 164cda8d54e..e69c877f243 100644
--- a/gcc/c-typeck.c
+++ b/gcc/c-typeck.c
@@ -8471,7 +8471,7 @@ c_finish_goto_ptr (location_t loc, tree expr)
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;
diff --git a/gcc/cgraphunit.c b/gcc/cgraphunit.c
index 0f3a6e23d00..e306b226115 100644
--- a/gcc/cgraphunit.c
+++ b/gcc/cgraphunit.c
@@ -539,8 +539,24 @@ cgraph_finalize_function (tree decl, bool nested)
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)
diff --git a/gcc/config/elfos.h b/gcc/config/elfos.h
index 8c415bad442..243d1e17635 100644
--- a/gcc/config/elfos.h
+++ b/gcc/config/elfos.h
@@ -280,7 +280,11 @@ see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
#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_FUNCTION_LABEL (FILE, NAME, DECL); \
} \
diff --git a/gcc/cp/ChangeLog.ifunc b/gcc/cp/ChangeLog.ifunc
new file mode 100644
index 00000000000..ce23cdf3f2c
--- /dev/null
+++ b/gcc/cp/ChangeLog.ifunc
@@ -0,0 +1,29 @@
+2009-07-05 H.J. Lu <hongjiu.lu@intel.com>
+
+ * call.c (standard_conversion): Support IFUNC member function.
+ * class.c (resolve_address_of_overloaded_function): Likewise.
+
+ * cp-tree.h (TYPE_PTRMEMIFUNC_P): New.
+
+ * typeck.c (cp_build_unary_op): Take the address of non-static
+ overloaded member function for IFUNC member function.
+
+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.
diff --git a/gcc/cp/call.c b/gcc/cp/call.c
index 0bc99bfa57d..e7d87a0e3c6 100644
--- a/gcc/cp/call.c
+++ b/gcc/cp/call.c
@@ -735,7 +735,12 @@ standard_conversion (tree to, tree from, tree expr, bool c_cast_p,
to = strip_top_quals (to);
from = strip_top_quals (from);
- if ((TYPE_PTRFN_P (to) || TYPE_PTRMEMFUNC_P (to))
+ if ((TYPE_PTRFN_P (to)
+ || TYPE_PTRMEMFUNC_P (to)
+ || (current_function_decl != NULL
+ && DECL_IS_IFUNC (current_function_decl)
+ && DECL_NONSTATIC_MEMBER_FUNCTION_P (current_function_decl)
+ && TYPE_PTRMEMIFUNC_P (to)))
&& expr && type_unknown_p (expr))
{
tsubst_flags_t tflags = tf_conv;
diff --git a/gcc/cp/class.c b/gcc/cp/class.c
index 73bcb7598ee..6597111955e 100644
--- a/gcc/cp/class.c
+++ b/gcc/cp/class.c
@@ -6223,6 +6223,7 @@ resolve_address_of_overloaded_function (tree target_type,
selected function. */
int is_ptrmem = 0;
+ int is_ifunc;
/* We store the matches in a TREE_LIST rooted here. The functions
are the TREE_PURPOSE, not the TREE_VALUE, in this list, for easy
interoperability with most_specialized_instantiation. */
@@ -6230,11 +6231,17 @@ resolve_address_of_overloaded_function (tree target_type,
tree fn;
tree target_fn_type;
+ is_ifunc = (current_function_decl != NULL
+ && DECL_IS_IFUNC (current_function_decl)
+ && DECL_NONSTATIC_MEMBER_FUNCTION_P (current_function_decl));
+
/* By the time we get here, we should be seeing only real
pointer-to-member types, not the internal POINTER_TYPE to
- METHOD_TYPE representation. */
+ METHOD_TYPE representation unless it is in an IFUNC member
+ function. */
gcc_assert (TREE_CODE (target_type) != POINTER_TYPE
- || TREE_CODE (TREE_TYPE (target_type)) != METHOD_TYPE);
+ || TREE_CODE (TREE_TYPE (target_type)) != METHOD_TYPE
+ || is_ifunc);
gcc_assert (is_overloaded_fn (overload));
@@ -6248,6 +6255,8 @@ resolve_address_of_overloaded_function (tree target_type,
/* This is OK, too. This comes from a conversion to reference
type. */
target_type = build_reference_type (target_type);
+ else if (is_ifunc && TYPE_PTRMEMIFUNC_P (target_type))
+ /* This is OK. */;
else
{
if (flags & tf_error)
@@ -6283,7 +6292,7 @@ resolve_address_of_overloaded_function (tree target_type,
continue;
if ((TREE_CODE (TREE_TYPE (fn)) == METHOD_TYPE)
- != is_ptrmem)
+ != (is_ptrmem | is_ifunc))
/* We're looking for a non-static member, and this isn't
one, or vice versa. */
continue;
@@ -6463,7 +6472,9 @@ resolve_address_of_overloaded_function (tree target_type,
perform_or_defer_access_check (access_path, fn, fn);
}
- if (TYPE_PTRFN_P (target_type) || TYPE_PTRMEMFUNC_P (target_type))
+ if (TYPE_PTRFN_P (target_type)
+ || TYPE_PTRMEMFUNC_P (target_type)
+ || (is_ifunc && TYPE_PTRMEMIFUNC_P (target_type)))
return cp_build_unary_op (ADDR_EXPR, fn, 0, flags);
else
{
diff --git a/gcc/cp/cp-tree.h b/gcc/cp/cp-tree.h
index 155db4ce4a8..548bf68496e 100644
--- a/gcc/cp/cp-tree.h
+++ b/gcc/cp/cp-tree.h
@@ -3276,6 +3276,12 @@ more_aggr_init_expr_args_p (const aggr_init_expr_arg_iterator *iter)
(TREE_CODE (NODE) == REFERENCE_TYPE \
&& TREE_CODE (TREE_TYPE (NODE)) == FUNCTION_TYPE)
+/* Returns true if NODE is a pointer to a member function inside
+ the IFUNC function body. */
+#define TYPE_PTRMEMIFUNC_P(NODE) \
+ (TREE_CODE (NODE) == POINTER_TYPE \
+ && TREE_CODE (TREE_TYPE (NODE)) == METHOD_TYPE)
+
/* Nonzero for _TYPE node means that this type is a pointer to member
function type. */
#define TYPE_PTRMEMFUNC_P(NODE) \
diff --git a/gcc/cp/decl.c b/gcc/cp/decl.c
index 3aaa20c0ed3..73cf2bc58f7 100644
--- a/gcc/cp/decl.c
+++ b/gcc/cp/decl.c
@@ -92,7 +92,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);
@@ -847,9 +847,23 @@ wrapup_globals_for_namespace (tree name_space, void* data)
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);
@@ -1646,6 +1660,25 @@ duplicate_decls (tree newdecl, tree olddecl, bool newdecl_is_friend)
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. */
@@ -11781,9 +11814,10 @@ lookup_enumerator (tree enumtype, tree name)
}
-/* We're defining DECL. Make sure that its type is OK. */
+/* We're defining DECL. Make sure that its 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);
@@ -11797,7 +11831,7 @@ check_function_type (tree decl, tree current_function_parms)
validate_constexpr_fundecl (decl);
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)))
{
@@ -11821,9 +11855,13 @@ check_function_type (tree decl, tree current_function_parms)
fntype = (cp_build_type_attribute_variant
(fntype, TYPE_ATTRIBUTES (TREE_TYPE (decl))));
TREE_TYPE (decl) = fntype;
+ 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.
@@ -11966,11 +12004,14 @@ start_preparsed_function (tree decl1, tree attrs, int flags)
/* 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;
@@ -12591,7 +12632,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;
@@ -12606,7 +12647,7 @@ finish_function (int flags)
record_key_method_defined (fndecl);
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
@@ -12700,7 +12741,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
@@ -12728,9 +12769,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
@@ -12742,8 +12784,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;
}
diff --git a/gcc/cp/typeck.c b/gcc/cp/typeck.c
index 484d299f462..8411c90c9e8 100644
--- a/gcc/cp/typeck.c
+++ b/gcc/cp/typeck.c
@@ -5201,9 +5201,20 @@ cp_build_unary_op (enum tree_code code, tree xarg, int noconvert,
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;
}
@@ -5264,6 +5275,17 @@ cp_build_unary_op (enum tree_code code, tree xarg, int noconvert,
if (TREE_CODE (argtype) == POINTER_TYPE
&& TREE_CODE (TREE_TYPE (argtype)) == METHOD_TYPE)
{
+ if (current_function_decl != NULL
+ && DECL_IS_IFUNC (current_function_decl)
+ && DECL_NONSTATIC_MEMBER_FUNCTION_P (current_function_decl)
+ && (TYPE_MAIN_VARIANT (argtype)
+ == TYPE_MAIN_VARIANT (function_return_type (current_function_decl))))
+ {
+ /* IFUNC member function returns the address of another
+ non-static member function. */
+ return val;
+ }
+
build_ptrmemfunc_type (argtype);
val = build_ptrmemfunc (argtype, val, 0,
/*c_cast_p=*/false,
@@ -7802,7 +7824,7 @@ check_return_expr (tree retval, bool *no_warning)
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)));
@@ -7828,7 +7850,7 @@ check_return_expr (tree retval, bool *no_warning)
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
@@ -7844,7 +7866,7 @@ check_return_expr (tree retval, bool *no_warning)
/* The variable must not have the `volatile' qualifier. */
&& !CP_TYPE_VOLATILE_P (TREE_TYPE (retval))
/* 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
diff --git a/gcc/doc/extend.texi b/gcc/doc/extend.texi
index 508c47624dd..0f64bcdadb0 100644
--- a/gcc/doc/extend.texi
+++ b/gcc/doc/extend.texi
@@ -1963,6 +1963,30 @@ is not defined in the same translation unit.
Not all target machines support this attribute.
+@item ifunc
+@cindex @code{ifunc} attribute
+The @code{ifunc} attribute only applies to a function definition, which
+causes the definition to be emitted as an indirect function. For
+instance,
+
+@smallexample
+int f (int) __attribute__ ((ifunc));
+@end smallexample
+
+@noindent
+
+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 the dynamic linker without any arguments.
+It is an error if @samp{f} is not defined 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 targets support this attribute.
+
@item aligned (@var{alignment})
@cindex @code{aligned} attribute
This attribute specifies a minimum alignment for the function,
diff --git a/gcc/gimplify.c b/gcc/gimplify.c
index 8b97ee3d0e8..db28dcd853c 100644
--- a/gcc/gimplify.c
+++ b/gcc/gimplify.c
@@ -1236,7 +1236,7 @@ gimplify_return_expr (tree stmt, gimple_seq *pre_p)
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
{
diff --git a/gcc/ipa-pure-const.c b/gcc/ipa-pure-const.c
index 1ff15bf22e6..5db496aa0c4 100644
--- a/gcc/ipa-pure-const.c
+++ b/gcc/ipa-pure-const.c
@@ -720,16 +720,22 @@ static funct_state
analyze_function (struct cgraph_node *fn, bool ipa)
{
tree decl = fn->decl;
- tree old_decl = current_function_decl;
+ tree old_decl;
funct_state l;
basic_block this_block;
l = XCNEW (struct funct_state_d);
- l->pure_const_state = IPA_CONST;
l->state_previously_known = IPA_NEITHER;
l->looping_previously_known = true;
l->looping = false;
l->can_throw = false;
+ if (DECL_IS_IFUNC (decl))
+ {
+ l->pure_const_state = IPA_NEITHER;
+ goto skip;
+ }
+ else
+ l->pure_const_state = IPA_CONST;
if (dump_file)
{
@@ -737,6 +743,8 @@ analyze_function (struct cgraph_node *fn, bool ipa)
cgraph_node_name (fn));
}
+ old_decl = current_function_decl;
+
push_cfun (DECL_STRUCT_FUNCTION (decl));
current_function_decl = decl;
@@ -810,6 +818,8 @@ end:
pop_cfun ();
current_function_decl = old_decl;
+
+skip:
if (dump_file)
{
if (l->looping)
diff --git a/gcc/stmt.c b/gcc/stmt.c
index 0b9ce8184f6..baa9cf1a23e 100644
--- a/gcc/stmt.c
+++ b/gcc/stmt.c
@@ -1634,7 +1634,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 ();
diff --git a/gcc/testsuite/ChangeLog.ifunc b/gcc/testsuite/ChangeLog.ifunc
new file mode 100644
index 00000000000..ff65dc97977
--- /dev/null
+++ b/gcc/testsuite/ChangeLog.ifunc
@@ -0,0 +1,71 @@
+2009-07-08 H.J. Lu <hongjiu.lu@intel.com>
+
+ * g++.dg/torture/ifunc-24.C: Swap scan-assembler.
+ * g++.dg/torture/ifunc-26.C: Likewise.
+
+ * g++.dg/torture/ifunc-27.C: New.
+ * g++.dg/torture/ifunc-28.C: Likewise.
+ * g++.dg/torture/ifunc-29.C: Likewise.
+ * g++.dg/torture/ifunc-30.C: Likewise.
+
+2009-07-06 H.J. Lu <hongjiu.lu@intel.com>
+
+ * g++.dg/torture/ifunc-26.C: New.
+
+2009-07-05 H.J. Lu <hongjiu.lu@intel.com>
+
+ * g++.dg/torture/ifunc-16.C: Add function name to scan-assembler.
+ * g++.dg/torture/ifunc-22.C: Likewise.
+
+ * g++.dg/torture/ifunc-21.C: Swap scan-assembler.
+
+ * g++.dg/torture/ifunc-24.C: New.
+ * g++.dg/torture/ifunc-25.C: Likewise.
+
+2009-07-05 H.J. Lu <hongjiu.lu@intel.com>
+
+ * g++.dg/torture/ifunc-21.C: New.
+ * g++.dg/torture/ifunc-22.C: Likewise.
+ * g++.dg/torture/ifunc-23.C: Likewise.
+
+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.
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-1.C b/gcc/testsuite/g++.dg/torture/ifunc-1.C
new file mode 100644
index 00000000000..77d42a80b88
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-1.C
@@ -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" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-10.C b/gcc/testsuite/g++.dg/torture/ifunc-10.C
new file mode 100644
index 00000000000..9c695f18ac7
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-10.C
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+int
+__attribute__ ((ifunc))
+foo (int, int)
+{
+}
+
+/* { dg-error "control reaches end of indirect function" "" { target *-*-* } 7 } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-11.C b/gcc/testsuite/g++.dg/torture/ifunc-11.C
new file mode 100644
index 00000000000..92673d6681e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-11.C
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+static int
+__attribute__ ((ifunc))
+foo (int, int)
+{
+}
+
+/* { dg-error "control reaches end of indirect function" "" { target *-*-* } 7 } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-12.C b/gcc/testsuite/g++.dg/torture/ifunc-12.C
new file mode 100644
index 00000000000..76d42cb2c07
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-12.C
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+void
+__attribute__ ((ifunc))
+foo (int, int)
+{
+}
+
+/* { dg-error "control reaches end of indirect function" "" { target *-*-* } 7 } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-13.C b/gcc/testsuite/g++.dg/torture/ifunc-13.C
new file mode 100644
index 00000000000..758673d30ca
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-13.C
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+static void
+__attribute__ ((ifunc))
+foo (int, int)
+{
+}
+
+/* { dg-error "control reaches end of indirect function" "" { target *-*-* } 7 } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-14.C b/gcc/testsuite/g++.dg/torture/ifunc-14.C
new file mode 100644
index 00000000000..0954118bc6f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-14.C
@@ -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 } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-15.C b/gcc/testsuite/g++.dg/torture/ifunc-15.C
new file mode 100644
index 00000000000..b29ae95148a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-15.C
@@ -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" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-16.C b/gcc/testsuite/g++.dg/torture/ifunc-16.C
new file mode 100644
index 00000000000..9db2968c775
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-16.C
@@ -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)\[ \]\+_ZN5ifunc3fooEf" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN5ifunc3fooEi, .gnu_indirect_function" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN5ifunc3fooEf, .gnu_indirect_function" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-17.C b/gcc/testsuite/g++.dg/torture/ifunc-17.C
new file mode 100644
index 00000000000..ccfb021186c
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-17.C
@@ -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" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-18.C b/gcc/testsuite/g++.dg/torture/ifunc-18.C
new file mode 100644
index 00000000000..3d5d902549f
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-18.C
@@ -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 } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-19.C b/gcc/testsuite/g++.dg/torture/ifunc-19.C
new file mode 100644
index 00000000000..a38ff212f3e
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-19.C
@@ -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 } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-2.C b/gcc/testsuite/g++.dg/torture/ifunc-2.C
new file mode 100644
index 00000000000..09acbd844e8
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-2.C
@@ -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" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-20.C b/gcc/testsuite/g++.dg/torture/ifunc-20.C
new file mode 100644
index 00000000000..1c088fa15ef
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-20.C
@@ -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" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-21.C b/gcc/testsuite/g++.dg/torture/ifunc-21.C
new file mode 100644
index 00000000000..058c96c3769
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-21.C
@@ -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 foo1 (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::foo1;
+}
+
+int
+bar (int x)
+{
+ return ifunc::foo (x);
+}
+
+int
+bar (float x)
+{
+ return ifunc::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" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-22.C b/gcc/testsuite/g++.dg/torture/ifunc-22.C
new file mode 100644
index 00000000000..56663b8a920
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-22.C
@@ -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 foo1 (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::foo1;
+}
+
+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)\[ \]\+_ZN5ifunc3fooEf" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN5ifunc3fooEi, .gnu_indirect_function" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN5ifunc3fooEf, .gnu_indirect_function" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-23.C b/gcc/testsuite/g++.dg/torture/ifunc-23.C
new file mode 100644
index 00000000000..1a30450681a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-23.C
@@ -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 foo1 (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::foo1;
+}
+
+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" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-24.C b/gcc/testsuite/g++.dg/torture/ifunc-24.C
new file mode 100644
index 00000000000..14c6a6c6b1a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-24.C
@@ -0,0 +1,49 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+template <class T1, class T2>
+class ifunc
+{
+private:
+ static int foo1 (T1);
+ static int foo1 (T2);
+
+public:
+ static int foo (T1);
+ static int foo (T2);
+};
+
+template <class T1, class T2>
+int
+__attribute__ ((ifunc))
+ifunc<T1, T2>::foo (T1)
+{
+ return &ifunc::foo1;
+}
+
+template <class T1, class T2>
+int
+__attribute__ ((ifunc))
+ifunc<T1, T2>::foo (T2)
+{
+ return &ifunc::foo1;
+}
+
+int
+bar (int x)
+{
+ return ifunc<int, float>::foo (x);
+}
+
+int
+bar (float x)
+{
+ return ifunc<int, float>::foo (x);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ \]\+_ZN5ifuncIifE3fooEi, .function" } } */
+/* { dg-final { scan-assembler-not ".type\[ \]\+_ZN5ifuncIifE3fooEf, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ \]\+_ZN5ifuncIifE3fooEi" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ \]\+_ZN5ifuncIifE3fooEf" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN5ifuncIifE3fooEi, .gnu_indirect_function" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN5ifuncIifE3fooEf, .gnu_indirect_function" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-25.C b/gcc/testsuite/g++.dg/torture/ifunc-25.C
new file mode 100644
index 00000000000..790b7292d22
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-25.C
@@ -0,0 +1,51 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+template <class T1, class T2>
+class ifunc
+{
+private:
+ int foo1 (T1);
+ int foo1 (T2);
+
+public:
+ int foo (T1);
+ int foo (T2);
+};
+
+template <class T1, class T2>
+int
+__attribute__ ((ifunc))
+ifunc<T1, T2>::foo (T1)
+{
+ return &ifunc<T1, T2>::foo1;
+}
+
+template <class T1, class T2>
+int
+__attribute__ ((ifunc))
+ifunc<T1, T2>::foo (T2)
+{
+ return &ifunc<T1, T2>::foo1;
+}
+
+int
+bar (int x)
+{
+ ifunc<int, float> i;
+ return i.foo (x);
+}
+
+int
+bar (float x)
+{
+ ifunc<int, float> i;
+ return i.foo (x);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ \]\+_ZN5ifuncIifE3fooEi, .function" } } */
+/* { dg-final { scan-assembler-not ".type\[ \]\+_ZN5ifuncIifE3fooEf, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ \]\+_ZN5ifuncIifE3fooEi" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ \]\+_ZN5ifuncIifE3fooEf" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN5ifuncIifE3fooEi, .gnu_indirect_function" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN5ifuncIifE3fooEf, .gnu_indirect_function" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-26.C b/gcc/testsuite/g++.dg/torture/ifunc-26.C
new file mode 100644
index 00000000000..4ee44ed5427
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-26.C
@@ -0,0 +1,51 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+template <class T1, class T2>
+class ifunc
+{
+private:
+ void foo1 (T1);
+ void foo1 (T2);
+
+public:
+ void foo (T1);
+ void foo (T2);
+};
+
+template <class T1, class T2>
+void
+__attribute__ ((ifunc))
+ifunc<T1, T2>::foo (T1)
+{
+ return &ifunc<T1, T2>::foo1;
+}
+
+template <class T1, class T2>
+void
+__attribute__ ((ifunc))
+ifunc<T1, T2>::foo (T2)
+{
+ return &ifunc<T1, T2>::foo1;
+}
+
+void
+bar (int x)
+{
+ ifunc<int, float> i;
+ i.foo (x);
+}
+
+void
+bar (float x)
+{
+ ifunc<int, float> i;
+ i.foo (x);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ \]\+_ZN5ifuncIifE3fooEi, .function" } } */
+/* { dg-final { scan-assembler-not ".type\[ \]\+_ZN5ifuncIifE3fooEf, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ \]\+_ZN5ifuncIifE3fooEi" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ \]\+_ZN5ifuncIifE3fooEf" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN5ifuncIifE3fooEi, .gnu_indirect_function" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN5ifuncIifE3fooEf, .gnu_indirect_function" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-27.C b/gcc/testsuite/g++.dg/torture/ifunc-27.C
new file mode 100644
index 00000000000..299b99d1c3a
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-27.C
@@ -0,0 +1,63 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+template <class T1, class T2>
+class ifunc
+{
+private:
+ virtual int foo1 (T1);
+ virtual int foo1 (T2);
+
+public:
+ virtual int foo (T1);
+ virtual int foo (T2);
+};
+
+template <class T1, class T2>
+class ifunc2 : public ifunc<T1, T2>
+{
+private:
+ int foo1 (T1);
+ int foo1 (T2);
+
+public:
+ int foo (T1);
+ int foo (T2);
+};
+
+template <class T1, class T2>
+int
+__attribute__ ((ifunc))
+ifunc2<T1, T2>::foo (T1)
+{
+ return &ifunc2<T1, T2>::foo1;
+}
+
+template <class T1, class T2>
+int
+__attribute__ ((ifunc))
+ifunc2<T1, T2>::foo (T2)
+{
+ return &ifunc2<T1, T2>::foo1;
+}
+
+int
+bar (int x)
+{
+ ifunc2<int, float> i;
+ return i.foo (x);
+}
+
+int
+bar (float x)
+{
+ ifunc2<int, float> i;
+ return i.foo (x);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ \]\+_ZN6ifunc2IifE3fooEi, .function" } } */
+/* { dg-final { scan-assembler-not ".type\[ \]\+_ZN6ifunc2IifE3fooEf, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ \]\+_ZN6ifunc2IifE3fooEi" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ \]\+_ZN6ifunc2IifE3fooEf" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN6ifunc2IifE3fooEi, .gnu_indirect_function" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN6ifunc2IifE3fooEf, .gnu_indirect_function" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-28.C b/gcc/testsuite/g++.dg/torture/ifunc-28.C
new file mode 100644
index 00000000000..03f35b76ccc
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-28.C
@@ -0,0 +1,63 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+template <class T1, class T2>
+class ifunc
+{
+private:
+ virtual void foo1 (T1);
+ virtual void foo1 (T2);
+
+public:
+ virtual void foo (T1);
+ virtual void foo (T2);
+};
+
+template <class T1, class T2>
+class ifunc2 : public ifunc<T1, T2>
+{
+private:
+ void foo1 (T1);
+ void foo1 (T2);
+
+public:
+ void foo (T1);
+ void foo (T2);
+};
+
+template <class T1, class T2>
+void
+__attribute__ ((ifunc))
+ifunc2<T1, T2>::foo (T1)
+{
+ return &ifunc2<T1, T2>::foo1;
+}
+
+template <class T1, class T2>
+void
+__attribute__ ((ifunc))
+ifunc2<T1, T2>::foo (T2)
+{
+ return &ifunc2<T1, T2>::foo1;
+}
+
+void
+bar (int x)
+{
+ ifunc2<int, float> i;
+ i.foo (x);
+}
+
+void
+bar (float x)
+{
+ ifunc2<int, float> i;
+ i.foo (x);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ \]\+_ZN6ifunc2IifE3fooEi, .function" } } */
+/* { dg-final { scan-assembler-not ".type\[ \]\+_ZN6ifunc2IifE3fooEf, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ \]\+_ZN6ifunc2IifE3fooEi" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ \]\+_ZN6ifunc2IifE3fooEf" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN6ifunc2IifE3fooEi, .gnu_indirect_function" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN6ifunc2IifE3fooEf, .gnu_indirect_function" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-29.C b/gcc/testsuite/g++.dg/torture/ifunc-29.C
new file mode 100644
index 00000000000..dc860fc9a9d
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-29.C
@@ -0,0 +1,59 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+class ifunc
+{
+private:
+ virtual int foo1 (int);
+ virtual float foo1 (float);
+
+public:
+ virtual int foo (int);
+ virtual float foo (float);
+};
+
+class ifunc2 : public ifunc
+{
+private:
+ int foo1 (int);
+ float foo1 (float);
+
+public:
+ int foo (int);
+ float foo (float);
+};
+
+int
+__attribute__ ((ifunc))
+ifunc2::foo (int)
+{
+ return &ifunc2::foo1;
+}
+
+float
+__attribute__ ((ifunc))
+ifunc2::foo (float)
+{
+ return &ifunc2::foo1;
+}
+
+int
+bar (int x)
+{
+ ifunc2 i;
+ return i.foo (x);
+}
+
+float
+bar (float x)
+{
+ ifunc2 i;
+ return i.foo (x);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ \]\+_ZN6ifunc23fooEi, .function" } } */
+/* { dg-final { scan-assembler-not ".type\[ \]\+_ZN6ifunc23fooEf, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ \]\+_ZN6ifunc23fooEi" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ \]\+_ZN6ifunc23fooEf" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN6ifunc23fooEi, .gnu_indirect_function" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN6ifunc23fooEf, .gnu_indirect_function" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-3.C b/gcc/testsuite/g++.dg/torture/ifunc-3.C
new file mode 100644
index 00000000000..69cfb7a5057
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-3.C
@@ -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" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-30.C b/gcc/testsuite/g++.dg/torture/ifunc-30.C
new file mode 100644
index 00000000000..9861ef6e110
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-30.C
@@ -0,0 +1,59 @@
+/* { dg-do compile { target i?86-*-linux* x86_64-*-linux* } } */
+/* { dg-options "-Wall -Wextra" } */
+
+class ifunc
+{
+private:
+ virtual void foo1 (int);
+ virtual void foo1 (float);
+
+public:
+ virtual void foo (int);
+ virtual void foo (float);
+};
+
+class ifunc2 : public ifunc
+{
+private:
+ void foo1 (int);
+ void foo1 (float);
+
+public:
+ void foo (int);
+ void foo (float);
+};
+
+void
+__attribute__ ((ifunc))
+ifunc2::foo (int)
+{
+ return &ifunc2::foo1;
+}
+
+void
+__attribute__ ((ifunc))
+ifunc2::foo (float)
+{
+ return &ifunc2::foo1;
+}
+
+void
+bar (int x)
+{
+ ifunc2 i;
+ i.foo (x);
+}
+
+void
+bar (float x)
+{
+ ifunc2 i;
+ i.foo (x);
+}
+
+/* { dg-final { scan-assembler-not ".type\[ \]\+_ZN6ifunc23fooEi, .function" } } */
+/* { dg-final { scan-assembler-not ".type\[ \]\+_ZN6ifunc23fooEf, .function" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ \]\+_ZN6ifunc23fooEi" } } */
+/* { dg-final { scan-assembler "(call|jmp)\[ \]\+_ZN6ifunc23fooEf" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN6ifunc23fooEi, .gnu_indirect_function" } } */
+/* { dg-final { scan-assembler ".type\[ \]\+_ZN6ifunc23fooEf, .gnu_indirect_function" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-4.C b/gcc/testsuite/g++.dg/torture/ifunc-4.C
new file mode 100644
index 00000000000..c4d5f4eb314
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-4.C
@@ -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" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-5.C b/gcc/testsuite/g++.dg/torture/ifunc-5.C
new file mode 100644
index 00000000000..18dd9e4cf62
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-5.C
@@ -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" } } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-6.C b/gcc/testsuite/g++.dg/torture/ifunc-6.C
new file mode 100644
index 00000000000..370597843d6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-6.C
@@ -0,0 +1,5 @@
+/* { dg-do compile } */
+
+int __attribute__ ((ifunc)) foo (int);
+
+/* { dg-error "indirect function .int foo.int.. never defined" "" { target *-*-* } 3 } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-7.C b/gcc/testsuite/g++.dg/torture/ifunc-7.C
new file mode 100644
index 00000000000..f105aefffd6
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-7.C
@@ -0,0 +1,5 @@
+/* { dg-do compile } */
+
+static int __attribute__ ((ifunc)) foo (int);
+
+/* { dg-error "indirect function .int foo.int.. never defined" "" { target *-*-* } 3 } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-8.C b/gcc/testsuite/g++.dg/torture/ifunc-8.C
new file mode 100644
index 00000000000..f9e9359f7c4
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-8.C
@@ -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 } */
diff --git a/gcc/testsuite/g++.dg/torture/ifunc-9.C b/gcc/testsuite/g++.dg/torture/ifunc-9.C
new file mode 100644
index 00000000000..c0e347015bd
--- /dev/null
+++ b/gcc/testsuite/g++.dg/torture/ifunc-9.C
@@ -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 } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-1.c b/gcc/testsuite/gcc.dg/torture/ifunc-1.c
new file mode 100644
index 00000000000..8ede0f9d0f1
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-1.c
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-10.c b/gcc/testsuite/gcc.dg/torture/ifunc-10.c
new file mode 100644
index 00000000000..9c695f18ac7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-10.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+int
+__attribute__ ((ifunc))
+foo (int, int)
+{
+}
+
+/* { dg-error "control reaches end of indirect function" "" { target *-*-* } 7 } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-11.c b/gcc/testsuite/gcc.dg/torture/ifunc-11.c
new file mode 100644
index 00000000000..92673d6681e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-11.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+static int
+__attribute__ ((ifunc))
+foo (int, int)
+{
+}
+
+/* { dg-error "control reaches end of indirect function" "" { target *-*-* } 7 } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-12.c b/gcc/testsuite/gcc.dg/torture/ifunc-12.c
new file mode 100644
index 00000000000..76d42cb2c07
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-12.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+void
+__attribute__ ((ifunc))
+foo (int, int)
+{
+}
+
+/* { dg-error "control reaches end of indirect function" "" { target *-*-* } 7 } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-13.c b/gcc/testsuite/gcc.dg/torture/ifunc-13.c
new file mode 100644
index 00000000000..758673d30ca
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-13.c
@@ -0,0 +1,9 @@
+/* { dg-do compile } */
+
+static void
+__attribute__ ((ifunc))
+foo (int, int)
+{
+}
+
+/* { dg-error "control reaches end of indirect function" "" { target *-*-* } 7 } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-14.c b/gcc/testsuite/gcc.dg/torture/ifunc-14.c
new file mode 100644
index 00000000000..0954118bc6f
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-14.c
@@ -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 } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-15.c b/gcc/testsuite/gcc.dg/torture/ifunc-15.c
new file mode 100644
index 00000000000..6b3a68438a4
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-15.c
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-16.c b/gcc/testsuite/gcc.dg/torture/ifunc-16.c
new file mode 100644
index 00000000000..1c62a164e19
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-16.c
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-17.c b/gcc/testsuite/gcc.dg/torture/ifunc-17.c
new file mode 100644
index 00000000000..55239f8e6bd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-17.c
@@ -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 } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-18.c b/gcc/testsuite/gcc.dg/torture/ifunc-18.c
new file mode 100644
index 00000000000..4b6ec2c8c45
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-18.c
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-2.c b/gcc/testsuite/gcc.dg/torture/ifunc-2.c
new file mode 100644
index 00000000000..31f2a7a98a8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-2.c
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-3.c b/gcc/testsuite/gcc.dg/torture/ifunc-3.c
new file mode 100644
index 00000000000..f57d18054d2
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-3.c
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-4.c b/gcc/testsuite/gcc.dg/torture/ifunc-4.c
new file mode 100644
index 00000000000..bd8a5f02641
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-4.c
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-5.c b/gcc/testsuite/gcc.dg/torture/ifunc-5.c
new file mode 100644
index 00000000000..e1e65865a95
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-5.c
@@ -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" } } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-6.c b/gcc/testsuite/gcc.dg/torture/ifunc-6.c
new file mode 100644
index 00000000000..57163ea0523
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-6.c
@@ -0,0 +1,5 @@
+/* { dg-do compile } */
+
+int __attribute__ ((ifunc)) foo (int);
+
+/* { dg-error "indirect function .foo. never defined" "" { target *-*-* } 3 } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-7.c b/gcc/testsuite/gcc.dg/torture/ifunc-7.c
new file mode 100644
index 00000000000..3b1064cba06
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-7.c
@@ -0,0 +1,5 @@
+/* { dg-do compile } */
+
+static int __attribute__ ((ifunc)) foo (int);
+
+/* { dg-error "indirect function .foo. never defined" "" { target *-*-* } 3 } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-8.c b/gcc/testsuite/gcc.dg/torture/ifunc-8.c
new file mode 100644
index 00000000000..3bab5d16ef8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-8.c
@@ -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 } */
diff --git a/gcc/testsuite/gcc.dg/torture/ifunc-9.c b/gcc/testsuite/gcc.dg/torture/ifunc-9.c
new file mode 100644
index 00000000000..c2ed2817f7e
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/torture/ifunc-9.c
@@ -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 } */
diff --git a/gcc/tree-cfg.c b/gcc/tree-cfg.c
index 413d7a9fb97..2a9e9d2d70e 100644
--- a/gcc/tree-cfg.c
+++ b/gcc/tree-cfg.c
@@ -3814,7 +3814,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. */
@@ -7294,10 +7294,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)
{
@@ -7309,7 +7310,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;
}
diff --git a/gcc/tree.h b/gcc/tree.h
index 32b15384f68..e0c9868ed90 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -3122,7 +3122,8 @@ struct GTY(()) tree_decl_with_vis {
unsigned init_priority_p : 1;
/* Used by C++ only. Might become a generic decl flag. */
unsigned shadowed_for_var_p : 1;
- /* 14 unused bits. */
+ unsigned ifunc_flag : 1;
+ /* 13 unused bits. */
};
extern tree decl_debug_expr_lookup (tree);
@@ -3170,6 +3171,10 @@ extern void decl_fini_priority_insert (tree, priority_type);
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)
+
#define DECL_VAR_ANN_PTR(NODE) \
(TREE_CODE (NODE) == VAR_DECL ? &(NODE)->var_decl.ann \
: TREE_CODE (NODE) == PARM_DECL ? &(NODE)->parm_decl.ann \
@@ -5578,6 +5583,18 @@ is_lang_specific (tree t)
return TREE_CODE (t) == LANG_TYPE || TREE_CODE (t) >= NUM_TREE_CODES;
}
+/* 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));
+}
+
/* In gimple-low.c. */
extern bool block_may_fallthru (const_tree);
diff --git a/gcc/varasm.c b/gcc/varasm.c
index 9a4c193e2e7..3f816babdb1 100644
--- a/gcc/varasm.c
+++ b/gcc/varasm.c
@@ -6787,8 +6787,13 @@ default_binds_local_p_1 (const_tree exp, int shlib)
{
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. */