diff options
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. */ |