===================================================================
@@ -1255,6 +1255,37 @@ DEFHOOK
"",
bool, (void), NULL)
+/* Target hook is used to compare the target attributes in two functions to
+ determine which function's features get higher priority. This is used
+ during function multi-versioning to figure out the order in which two
+ versions must be dispatched. A function version with a higher priority
+ is checked for dispatching earlier. DECL1 and DECL2 are
+ the two function decls that will be compared. It returns positive value
+ if DECL1 is higher priority, negative value if DECL2 is higher priority
+ and 0 if they are the same. */
+DEFHOOK
+(compare_version_priority,
+ "",
+ int, (tree decl1, tree decl2), NULL)
+
+/* Target hook is used to generate the dispatcher logic to invoke the right
+ function version at run-time for a given set of function versions.
+ ARG points to the callgraph node of the dispatcher function whose body
+ must be generated. */
+DEFHOOK
+(generate_version_dispatcher_body,
+ "",
+ tree, (void *arg), NULL)
+
+/* Target hook is used to get the dispatcher function for a set of function
+ versions. The dispatcher function is called to invoke the right function
+ version at run-time. DECL is one version from a set of semantically
+ identical versions. */
+DEFHOOK
+(get_function_versions_dispatcher,
+ "",
+ tree, (void *decl), NULL)
+
/* Returns a code for a target-specific builtin that implements
reciprocal of the function, or NULL_TREE if not available. */
DEFHOOK
@@ -2648,6 +2679,16 @@ DEFHOOK
void, (void),
hook_void_void)
+/* This function returns true if DECL1 and DECL2 are versions of the same
+ function. DECL1 and DECL2 are function versions if and only if they
+ have the same function signature and different target specific attributes,
+ that is, they are compiled for different target machines. */
+DEFHOOK
+(function_versions,
+ "",
+ bool, (tree decl1, tree decl2),
+ hook_bool_tree_tree_false)
+
/* Function to determine if one function can inline another function. */
#undef HOOK_PREFIX
#define HOOK_PREFIX "TARGET_"
===================================================================
@@ -670,9 +670,13 @@ check_classfn (tree ctype, tree function, tree tem
if (is_template != (TREE_CODE (fndecl) == TEMPLATE_DECL))
continue;
+ /* While finding a match, same types and params are not enough
+ if the function is versioned. Also check version ("target")
+ attributes. */
if (same_type_p (TREE_TYPE (TREE_TYPE (function)),
TREE_TYPE (TREE_TYPE (fndecl)))
&& compparms (p1, p2)
+ && !targetm.target_option.function_versions (function, fndecl)
&& (!is_template
|| comp_template_parms (template_parms,
DECL_TEMPLATE_PARMS (fndecl)))
===================================================================
@@ -42,6 +42,7 @@ along with GCC; see the file COPYING3. If not see
#include "c-family/c-objc.h"
#include "timevar.h"
#include "tree-threadsafe-analyze.h"
+#include "cgraph.h"
/* The various kinds of conversion. */
@@ -6313,6 +6314,63 @@ magic_varargs_p (tree fn)
return false;
}
+/* Returns the decl of the dispatcher function if FN is a function version. */
+
+tree
+get_function_version_dispatcher (tree fn)
+{
+ tree dispatcher_decl = NULL;
+
+ gcc_assert (TREE_CODE (fn) == FUNCTION_DECL
+ && DECL_FUNCTION_VERSIONED (fn));
+
+ gcc_assert (targetm.get_function_versions_dispatcher);
+ dispatcher_decl = targetm.get_function_versions_dispatcher (fn);
+
+ if (dispatcher_decl == NULL)
+ {
+ error_at (input_location, "use of multiversioned function "
+ "without a default");
+ return NULL;
+ }
+
+ retrofit_lang_decl (dispatcher_decl);
+ gcc_assert (dispatcher_decl != NULL);
+ return dispatcher_decl;
+}
+
+/* fn is a function version dispatcher that is marked used. Mark all the
+ semantically identical function versions it will dispatch as used. */
+
+void
+mark_versions_used (tree fn)
+{
+ struct cgraph_node *node;
+ struct cgraph_function_version_info *node_v;
+ struct cgraph_function_version_info *it_v;
+
+ gcc_assert (TREE_CODE (fn) == FUNCTION_DECL);
+
+ node = cgraph_get_node (fn);
+ if (node == NULL)
+ return;
+
+ gcc_assert (node->dispatcher_function);
+
+ node_v = get_cgraph_node_version (node);
+ if (node_v == NULL)
+ return;
+
+ /* All semantically identical versions are chained. Traverse and mark each
+ one of them as used. */
+ it_v = node_v->next;
+ while (it_v != NULL)
+ {
+ mark_used (it_v->this_node->decl);
+ it_v = it_v->next;
+ }
+}
+
/* Subroutine of the various build_*_call functions. Overload resolution
has chosen a winning candidate CAND; build up a CALL_EXPR accordingly.
ARGS is a TREE_LIST of the unconverted arguments to the call. FLAGS is a
@@ -6762,6 +6820,22 @@ build_over_call (struct z_candidate *cand, int fla
return fold_convert (void_type_node, argarray[0]);
/* FIXME handle trivial default constructor, too. */
+ /* For calls to a multi-versioned function, overload resolution
+ returns the function with the highest target priority, that is,
+ the version that will checked for dispatching first. If this
+ version is inlinable, a direct call to this version can be made
+ otherwise the call should go through the dispatcher. */
+
+ if (DECL_FUNCTION_VERSIONED (fn)
+ && !targetm.target_option.can_inline_p (current_function_decl, fn))
+ {
+ fn = get_function_version_dispatcher (fn);
+ if (fn == NULL)
+ return NULL;
+ if (!already_used)
+ mark_versions_used (fn);
+ }
+
if (!already_used)
mark_used (fn);
@@ -8306,6 +8380,38 @@ joust (struct z_candidate *cand1, struct z_candida
}
}
+ /* For candidates of a multi-versioned function, make the version with
+ the highest priority win. This version will be checked for dispatching
+ first. If this version can be inlined into the caller, the front-end
+ will simply make a direct call to this function. */
+
+ if (TREE_CODE (cand1->fn) == FUNCTION_DECL
+ && DECL_FUNCTION_VERSIONED (cand1->fn)
+ && TREE_CODE (cand2->fn) == FUNCTION_DECL
+ && DECL_FUNCTION_VERSIONED (cand2->fn))
+ {
+ tree f1 = TREE_TYPE (cand1->fn);
+ tree f2 = TREE_TYPE (cand2->fn);
+ tree p1 = TYPE_ARG_TYPES (f1);
+ tree p2 = TYPE_ARG_TYPES (f2);
+
+ /* Check if cand1->fn and cand2->fn are versions of the same function. It
+ is possible that cand1->fn and cand2->fn are function versions but of
+ different functions. Check types to see if they are versions of the same
+ function. */
+ if (compparms (p1, p2)
+ && same_type_p (TREE_TYPE (f1), TREE_TYPE (f2)))
+ {
+ /* Always make the version with the higher priority, more
+ specialized, win. */
+ gcc_assert (targetm.compare_version_priority);
+ if (targetm.compare_version_priority (cand1->fn, cand2->fn) >= 0)
+ return 1;
+ else
+ return -1;
+ }
+ }
+
/* If the two function declarations represent the same function (this can
happen with declarations in multiple scopes and arg-dependent lookup),
arbitrarily choose one. But first make sure the default args we're
===================================================================
@@ -4904,6 +4904,8 @@ extern bool is_list_ctor (tree);
#ifdef ENABLE_CHECKING
extern void validate_conversion_obstack (void);
#endif /* ENABLE_CHECKING */
+extern void mark_versions_used (tree);
+extern tree get_function_version_dispatcher (tree);
/* in class.c */
extern tree build_vfield_ref (tree, tree);
===================================================================
@@ -1093,6 +1093,33 @@ add_method (tree type, tree method, tree using_dec
|| same_type_p (TREE_TYPE (fn_type),
TREE_TYPE (method_type))))
{
+ /* For function versions, their parms and types match
+ but they are not duplicates. Record function versions
+ as and when they are found. extern "C" functions are
+ not treated as versions. */
+ if (TREE_CODE (fn) == FUNCTION_DECL
+ && TREE_CODE (method) == FUNCTION_DECL
+ && !DECL_EXTERN_C_P (fn)
+ && !DECL_EXTERN_C_P (method)
+ && targetm.target_option.function_versions (fn, method))
+ {
+ /* Mark functions as versions if necessary. Modify the mangled
+ decl name if necessary. */
+ if (!DECL_FUNCTION_VERSIONED (fn))
+ {
+ DECL_FUNCTION_VERSIONED (fn) = 1;
+ if (DECL_ASSEMBLER_NAME_SET_P (fn))
+ mangle_decl (fn);
+ }
+ if (!DECL_FUNCTION_VERSIONED (method))
+ {
+ DECL_FUNCTION_VERSIONED (method) = 1;
+ if (DECL_ASSEMBLER_NAME_SET_P (method))
+ mangle_decl (method);
+ }
+ record_function_versions (fn, method);
+ continue;
+ }
if (using_decl)
{
if (DECL_CONTEXT (fn) == type)
@@ -7033,12 +7060,17 @@ resolve_address_of_overloaded_function (tree targe
{
/* There were too many matches. First check if they're all
the same function. */
- tree match;
+ tree match = NULL_TREE;
fn = TREE_PURPOSE (matches);
+
+ /* For multi-versioned functions, more than one match is just fine and
+ decls_match will return false as they are different. */
for (match = TREE_CHAIN (matches); match; match = TREE_CHAIN (match))
- if (!decls_match (fn, TREE_PURPOSE (match)))
- break;
+ if (!decls_match (fn, TREE_PURPOSE (match))
+ && !targetm.target_option.function_versions
+ (fn, TREE_PURPOSE (match)))
+ break;
if (match)
{
@@ -7079,6 +7111,20 @@ resolve_address_of_overloaded_function (tree targe
}
}
+ /* If a pointer to a function that is multi-versioned is requested, the
+ pointer to the dispatcher function is returned instead. This works
+ well because indirectly calling the function will dispatch the right
+ function version at run-time. */
+ if (DECL_FUNCTION_VERSIONED (fn))
+ {
+ fn = get_function_version_dispatcher (fn);
+ if (fn == NULL)
+ return error_mark_node;
+ /* Mark all the versions corresponding to the dispatcher as used. */
+ if (!(flags & tf_conv))
+ mark_versions_used (fn);
+ }
+
/* If we're doing overload resolution purely for the purpose of
determining conversion sequences, we should not consider the
function used. If this conversion sequence is selected, the
===================================================================
@@ -55,6 +55,7 @@ along with GCC; see the file COPYING3. If not see
#include "splay-tree.h"
#include "cgraph.h"
#include "plugin.h"
+#include "cgraph.h"
/* Possible cases of bad specifiers type used by bad_specifiers. */
enum bad_spec_place {
@@ -974,6 +975,36 @@ decls_match (tree newdecl, tree olddecl)
if (t1 != t2)
return 0;
+ /* The decls dont match if they correspond to two different versions
+ of the same function. Disallow extern "C" functions to be
+ versions for now. */
+ if (compparms (p1, p2)
+ && same_type_p (TREE_TYPE (f1), TREE_TYPE (f2))
+ && !DECL_EXTERN_C_P (newdecl)
+ && !DECL_EXTERN_C_P (olddecl)
+ && targetm.target_option.function_versions (newdecl, olddecl))
+ {
+ /* Mark functions as versions if necessary. Modify the mangled decl
+ name if necessary. */
+ if (DECL_FUNCTION_VERSIONED (newdecl)
+ && DECL_FUNCTION_VERSIONED (olddecl))
+ return 0;
+ if (!DECL_FUNCTION_VERSIONED (newdecl))
+ {
+ DECL_FUNCTION_VERSIONED (newdecl) = 1;
+ if (DECL_ASSEMBLER_NAME_SET_P (newdecl))
+ mangle_decl (newdecl);
+ }
+ if (!DECL_FUNCTION_VERSIONED (olddecl))
+ {
+ DECL_FUNCTION_VERSIONED (olddecl) = 1;
+ if (DECL_ASSEMBLER_NAME_SET_P (olddecl))
+ mangle_decl (olddecl);
+ }
+ record_function_versions (olddecl, newdecl);
+ return 0;
+ }
+
if (CP_DECL_CONTEXT (newdecl) != CP_DECL_CONTEXT (olddecl)
&& ! (DECL_EXTERN_C_P (newdecl)
&& DECL_EXTERN_C_P (olddecl)))
@@ -1484,7 +1515,11 @@ duplicate_decls (tree newdecl, tree olddecl, bool
error ("previous declaration %q+#D here", olddecl);
return NULL_TREE;
}
- else if (compparms (TYPE_ARG_TYPES (TREE_TYPE (newdecl)),
+ /* For function versions, params and types match, but they
+ are not ambiguous. */
+ else if ((!DECL_FUNCTION_VERSIONED (newdecl)
+ && !DECL_FUNCTION_VERSIONED (olddecl))
+ && compparms (TYPE_ARG_TYPES (TREE_TYPE (newdecl)),
TYPE_ARG_TYPES (TREE_TYPE (olddecl))))
{
error ("new declaration %q#D", newdecl);
@@ -2252,6 +2287,18 @@ duplicate_decls (tree newdecl, tree olddecl, bool
else if (DECL_PRESERVE_P (newdecl))
DECL_PRESERVE_P (olddecl) = 1;
+ /* Merge the DECL_FUNCTION_VERSIONED information. newdecl will be copied
+ to olddecl and deleted. */
+ if (TREE_CODE (newdecl) == FUNCTION_DECL
+ && DECL_FUNCTION_VERSIONED (olddecl))
+ {
+ /* Set the flag for newdecl so that it gets copied to olddecl. */
+ DECL_FUNCTION_VERSIONED (newdecl) = 1;
+ /* newdecl will be purged after copying to olddecl and is no longer
+ a version. */
+ delete_function_version (newdecl);
+ }
+
if (TREE_CODE (newdecl) == FUNCTION_DECL)
{
int function_size;
===================================================================
@@ -62,6 +62,8 @@ along with GCC; see the file COPYING3. If not see
#include "opts.h"
#include "diagnostic.h"
#include "cfgloop.h"
+#include "tree-pass.h"
+#include "tree-flow.h"
enum upper_128bits_state
{
@@ -4216,7 +4218,10 @@ ix86_valid_target_attribute_inner_p (tree args, ch
}
else if (TREE_CODE (args) != STRING_CST)
- gcc_unreachable ();
+ {
+ error ("attribute %<target%> argument not a string");
+ return false;
+ }
/* Handle multiple arguments separated by commas. */
next_optstr = ASTRDUP (TREE_STRING_POINTER (args));
@@ -4361,7 +4366,7 @@ ix86_valid_target_attribute_tree (tree args)
/* Process each of the options on the chain. */
if (! ix86_valid_target_attribute_inner_p (args, option_strings,
&enum_opts_set))
- return NULL_TREE;
+ return error_mark_node;
/* If the changed options are different from the default, rerun
ix86_option_override_internal, and then save the options away.
@@ -4426,6 +4431,15 @@ ix86_valid_target_attribute_p (tree fndecl,
{
struct cl_target_option cur_target;
bool ret = true;
+
+ /* attribute((target("default"))) does nothing, beyond
+ affecting multi-versioning. */
+ if (TREE_VALUE (args)
+ && TREE_CODE (TREE_VALUE (args)) == STRING_CST
+ && TREE_CHAIN (args) == NULL_TREE
+ && strcmp (TREE_STRING_POINTER (TREE_VALUE (args)), "default") == 0)
+ return true;
+
tree old_optimize = build_optimization_node ();
tree new_target, new_optimize;
tree func_optimize = DECL_FUNCTION_SPECIFIC_OPTIMIZATION (fndecl);
@@ -4442,10 +4456,10 @@ ix86_valid_target_attribute_p (tree fndecl,
new_target = ix86_valid_target_attribute_tree (args);
new_optimize = build_optimization_node ();
- if (!new_target)
+ if (new_target == error_mark_node)
ret = false;
- else if (fndecl)
+ else if (fndecl && new_target)
{
DECL_FUNCTION_SPECIFIC_TARGET (fndecl) = new_target;
@@ -27983,6 +27997,1016 @@ ix86_slow_unaligned_vector_memop (void)
return false;
}
+/* This adds a condition to the basic_block NEW_BB in function FUNCTION_DECL
+ to return a pointer to VERSION_DECL if the outcome of the expression
+ formed by PREDICATE_CHAIN is true. This function will be called during
+ version dispatch to decide which function version to execute. It returns
+ the basic block at the end, to which more conditions can be added. */
+
+static basic_block
+add_condition_to_bb (tree function_decl, tree version_decl,
+ tree predicate_chain, basic_block new_bb)
+{
+ gimple return_stmt;
+ tree convert_expr, result_var;
+ gimple convert_stmt;
+ gimple call_cond_stmt;
+ gimple if_else_stmt;
+
+ basic_block bb1, bb2, bb3;
+ edge e12, e23;
+
+ tree cond_var, and_expr_var = NULL_TREE;
+ gimple_seq gseq;
+
+ tree predicate_decl, predicate_arg;
+
+ push_cfun (DECL_STRUCT_FUNCTION (function_decl));
+
+ gcc_assert (new_bb != NULL);
+ gseq = bb_seq (new_bb);
+
+
+ convert_expr = build1 (CONVERT_EXPR, ptr_type_node,
+ build_fold_addr_expr (version_decl));
+ result_var = create_tmp_var (ptr_type_node, NULL);
+ convert_stmt = gimple_build_assign (result_var, convert_expr);
+ return_stmt = gimple_build_return (result_var);
+
+ if (predicate_chain == NULL_TREE)
+ {
+ gimple_seq_add_stmt (&gseq, convert_stmt);
+ gimple_seq_add_stmt (&gseq, return_stmt);
+ set_bb_seq (new_bb, gseq);
+ gimple_set_bb (convert_stmt, new_bb);
+ gimple_set_bb (return_stmt, new_bb);
+ pop_cfun ();
+ return new_bb;
+ }
+
+ while (predicate_chain != NULL)
+ {
+ cond_var = create_tmp_var (integer_type_node, NULL);
+ predicate_decl = TREE_PURPOSE (predicate_chain);
+ predicate_arg = TREE_VALUE (predicate_chain);
+ call_cond_stmt = gimple_build_call (predicate_decl, 1, predicate_arg);
+ gimple_call_set_lhs (call_cond_stmt, cond_var);
+
+ gimple_set_block (call_cond_stmt, DECL_INITIAL (function_decl));
+ gimple_set_bb (call_cond_stmt, new_bb);
+ gimple_seq_add_stmt (&gseq, call_cond_stmt);
+
+ predicate_chain = TREE_CHAIN (predicate_chain);
+
+ if (and_expr_var == NULL)
+ and_expr_var = cond_var;
+ else
+ {
+ gimple assign_stmt;
+ /* Use MIN_EXPR to check if any integer is zero?.
+ and_expr_var = min_expr <cond_var, and_expr_var> */
+ assign_stmt = gimple_build_assign (and_expr_var,
+ build2 (MIN_EXPR, integer_type_node,
+ cond_var, and_expr_var));
+
+ gimple_set_block (assign_stmt, DECL_INITIAL (function_decl));
+ gimple_set_bb (assign_stmt, new_bb);
+ gimple_seq_add_stmt (&gseq, assign_stmt);
+ }
+ }
+
+ if_else_stmt = gimple_build_cond (GT_EXPR, and_expr_var,
+ integer_zero_node,
+ NULL_TREE, NULL_TREE);
+ gimple_set_block (if_else_stmt, DECL_INITIAL (function_decl));
+ gimple_set_bb (if_else_stmt, new_bb);
+ gimple_seq_add_stmt (&gseq, if_else_stmt);
+
+ gimple_seq_add_stmt (&gseq, convert_stmt);
+ gimple_seq_add_stmt (&gseq, return_stmt);
+ set_bb_seq (new_bb, gseq);
+
+ bb1 = new_bb;
+ e12 = split_block (bb1, if_else_stmt);
+ bb2 = e12->dest;
+ e12->flags &= ~EDGE_FALLTHRU;
+ e12->flags |= EDGE_TRUE_VALUE;
+
+ e23 = split_block (bb2, return_stmt);
+
+ gimple_set_bb (convert_stmt, bb2);
+ gimple_set_bb (return_stmt, bb2);
+
+ bb3 = e23->dest;
+ make_edge (bb1, bb3, EDGE_FALSE_VALUE);
+
+ remove_edge (e23);
+ make_edge (bb2, EXIT_BLOCK_PTR, 0);
+
+ pop_cfun ();
+
+ return bb3;
+}
+
+/* This parses the attribute arguments to target in DECL and determines
+ the right builtin to use to match the platform specification.
+ It returns the priority value for this version decl. If PREDICATE_LIST
+ is not NULL, it stores the list of cpu features that need to be checked
+ before dispatching this function. */
+
+static unsigned int
+get_builtin_code_for_version (tree decl, tree *predicate_list)
+{
+ tree attrs;
+ struct cl_target_option cur_target;
+ tree target_node;
+ struct cl_target_option *new_target;
+ const char *arg_str = NULL;
+ const char *attrs_str = NULL;
+ char *tok_str = NULL;
+ char *token;
+
+ /* Priority of i386 features, greater value is higher priority. This is
+ used to decide the order in which function dispatch must happen. For
+ instance, a version specialized for SSE4.2 should be checked for dispatch
+ before a version for SSE3, as SSE4.2 implies SSE3. */
+ enum feature_priority
+ {
+ P_ZERO = 0,
+ P_MMX,
+ P_SSE,
+ P_SSE2,
+ P_SSE3,
+ P_SSSE3,
+ P_PROC_SSSE3,
+ P_SSE4_a,
+ P_PROC_SSE4_a,
+ P_SSE4_1,
+ P_SSE4_2,
+ P_PROC_SSE4_2,
+ P_POPCNT,
+ P_AVX,
+ P_AVX2,
+ P_FMA,
+ P_PROC_FMA
+ };
+
+ enum feature_priority priority = P_ZERO;
+
+ /* These are the target attribute strings for which a dispatcher is
+ available, from fold_builtin_cpu. */
+
+ static struct _feature_list
+ {
+ const char *const name;
+ const enum feature_priority priority;
+ }
+ const feature_list[] =
+ {
+ {"mmx", P_MMX},
+ {"sse", P_SSE},
+ {"sse2", P_SSE2},
+ {"sse3", P_SSE3},
+ {"ssse3", P_SSSE3},
+ {"sse4.1", P_SSE4_1},
+ {"sse4.2", P_SSE4_2},
+ {"popcnt", P_POPCNT},
+ {"avx", P_AVX},
+ {"avx2", P_AVX2}
+ };
+
+
+ static unsigned int NUM_FEATURES
+ = sizeof (feature_list) / sizeof (struct _feature_list);
+
+ unsigned int i;
+
+ tree predicate_chain = NULL_TREE;
+ tree predicate_decl, predicate_arg;
+
+ attrs = lookup_attribute ("target", DECL_ATTRIBUTES (decl));
+ gcc_assert (attrs != NULL);
+
+ attrs = TREE_VALUE (TREE_VALUE (attrs));
+
+ gcc_assert (TREE_CODE (attrs) == STRING_CST);
+ attrs_str = TREE_STRING_POINTER (attrs);
+
+ /* Return priority zero for default function. */
+ if (strcmp (attrs_str, "default") == 0)
+ return 0;
+
+ /* Handle arch= if specified. For priority, set it to be 1 more than
+ the best instruction set the processor can handle. For instance, if
+ there is a version for atom and a version for ssse3 (the highest ISA
+ priority for atom), the atom version must be checked for dispatch
+ before the ssse3 version. */
+ if (strstr (attrs_str, "arch=") != NULL)
+ {
+ cl_target_option_save (&cur_target, &global_options);
+ target_node = ix86_valid_target_attribute_tree (attrs);
+
+ gcc_assert (target_node);
+ new_target = TREE_TARGET_OPTION (target_node);
+ gcc_assert (new_target);
+
+ if (new_target->arch_specified && new_target->arch > 0)
+ {
+ switch (new_target->arch)
+ {
+ case PROCESSOR_CORE2_32:
+ case PROCESSOR_CORE2_64:
+ arg_str = "core2";
+ priority = P_PROC_SSSE3;
+ break;
+ case PROCESSOR_COREI7_32:
+ case PROCESSOR_COREI7_64:
+ arg_str = "corei7";
+ priority = P_PROC_SSE4_2;
+ break;
+ case PROCESSOR_ATOM:
+ arg_str = "atom";
+ priority = P_PROC_SSSE3;
+ break;
+ case PROCESSOR_AMDFAM10:
+ arg_str = "amdfam10h";
+ priority = P_PROC_SSE4_a;
+ break;
+ case PROCESSOR_BDVER1:
+ arg_str = "bdver1";
+ priority = P_PROC_FMA;
+ break;
+ case PROCESSOR_BDVER2:
+ arg_str = "bdver2";
+ priority = P_PROC_FMA;
+ break;
+ }
+ }
+
+ cl_target_option_restore (&global_options, &cur_target);
+
+ if (predicate_list && arg_str == NULL)
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "No dispatcher found for the versioning attributes");
+ return 0;
+ }
+
+ if (predicate_list)
+ {
+ predicate_decl = ix86_builtins [(int) IX86_BUILTIN_CPU_IS];
+ /* For a C string literal the length includes the trailing NULL. */
+ predicate_arg = build_string_literal (strlen (arg_str) + 1, arg_str);
+ predicate_chain = tree_cons (predicate_decl, predicate_arg,
+ predicate_chain);
+ }
+ }
+
+ /* Process feature name. */
+ tok_str = (char *) xmalloc (strlen (attrs_str) + 1);
+ strcpy (tok_str, attrs_str);
+ token = strtok (tok_str, ",");
+ predicate_decl = ix86_builtins [(int) IX86_BUILTIN_CPU_SUPPORTS];
+
+ while (token != NULL)
+ {
+ /* Do not process "arch=" */
+ if (strncmp (token, "arch=", 5) == 0)
+ {
+ token = strtok (NULL, ",");
+ continue;
+ }
+ for (i = 0; i < NUM_FEATURES; ++i)
+ {
+ if (strcmp (token, feature_list[i].name) == 0)
+ {
+ if (predicate_list)
+ {
+ predicate_arg = build_string_literal (
+ strlen (feature_list[i].name) + 1,
+ feature_list[i].name);
+ predicate_chain = tree_cons (predicate_decl, predicate_arg,
+ predicate_chain);
+ }
+ /* Find the maximum priority feature. */
+ if (feature_list[i].priority > priority)
+ priority = feature_list[i].priority;
+
+ break;
+ }
+ }
+ if (predicate_list && i == NUM_FEATURES)
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "No dispatcher found for %s", token);
+ return 0;
+ }
+ token = strtok (NULL, ",");
+ }
+ free (tok_str);
+
+ if (predicate_list && predicate_chain == NULL_TREE)
+ {
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "No dispatcher found for the versioning attributes : %s",
+ attrs_str);
+ return 0;
+ }
+ else if (predicate_list)
+ {
+ predicate_chain = nreverse (predicate_chain);
+ *predicate_list = predicate_chain;
+ }
+
+ return priority;
+}
+
+/* This compares the priority of target features in function DECL1
+ and DECL2. It returns positive value if DECL1 is higher priority,
+ negative value if DECL2 is higher priority and 0 if they are the
+ same. */
+
+static int
+ix86_compare_version_priority (tree decl1, tree decl2)
+{
+ unsigned int priority1 = get_builtin_code_for_version (decl1, NULL);
+ unsigned int priority2 = get_builtin_code_for_version (decl2, NULL);
+
+ return (int)priority1 - (int)priority2;
+}
+
+/* V1 and V2 point to function versions with different priorities
+ based on the target ISA. This function compares their priorities. */
+
+static int
+feature_compare (const void *v1, const void *v2)
+{
+ typedef struct _function_version_info
+ {
+ tree version_decl;
+ tree predicate_chain;
+ unsigned int dispatch_priority;
+ } function_version_info;
+
+ const function_version_info c1 = *(const function_version_info *)v1;
+ const function_version_info c2 = *(const function_version_info *)v2;
+ return (c2.dispatch_priority - c1.dispatch_priority);
+}
+
+/* This function generates the dispatch function for
+ multi-versioned functions. DISPATCH_DECL is the function which will
+ contain the dispatch logic. FNDECLS are the function choices for
+ dispatch, and is a tree chain. EMPTY_BB is the basic block pointer
+ in DISPATCH_DECL in which the dispatch code is generated. */
+
+static int
+dispatch_function_versions (tree dispatch_decl,
+ void *fndecls_p,
+ basic_block *empty_bb)
+{
+ tree default_decl;
+ gimple ifunc_cpu_init_stmt;
+ gimple_seq gseq;
+ int ix;
+ tree ele;
+ VEC (tree, heap) *fndecls;
+ unsigned int num_versions = 0;
+ unsigned int actual_versions = 0;
+ unsigned int i;
+
+ struct _function_version_info
+ {
+ tree version_decl;
+ tree predicate_chain;
+ unsigned int dispatch_priority;
+ }*function_version_info;
+
+ gcc_assert (dispatch_decl != NULL
+ && fndecls_p != NULL
+ && empty_bb != NULL);
+
+ /*fndecls_p is actually a vector. */
+ fndecls = (VEC (tree, heap) *)fndecls_p;
+
+ /* At least one more version other than the default. */
+ num_versions = VEC_length (tree, fndecls);
+ gcc_assert (num_versions >= 2);
+
+ function_version_info = (struct _function_version_info *)
+ XNEWVEC (struct _function_version_info, (num_versions - 1));
+
+ /* The first version in the vector is the default decl. */
+ default_decl = VEC_index (tree, fndecls, 0);
+
+ push_cfun (DECL_STRUCT_FUNCTION (dispatch_decl));
+
+ gseq = bb_seq (*empty_bb);
+ /* Function version dispatch is via IFUNC. IFUNC resolvers fire before
+ constructors, so explicity call __builtin_cpu_init here. */
+ ifunc_cpu_init_stmt = gimple_build_call_vec (
+ ix86_builtins [(int) IX86_BUILTIN_CPU_INIT], NULL);
+ gimple_seq_add_stmt (&gseq, ifunc_cpu_init_stmt);
+ gimple_set_bb (ifunc_cpu_init_stmt, *empty_bb);
+ set_bb_seq (*empty_bb, gseq);
+
+ pop_cfun ();
+
+
+ for (ix = 1; VEC_iterate (tree, fndecls, ix, ele); ++ix)
+ {
+ tree version_decl = ele;
+ tree predicate_chain = NULL_TREE;
+ unsigned int priority;
+ /* Get attribute string, parse it and find the right predicate decl.
+ The predicate function could be a lengthy combination of many
+ features, like arch-type and various isa-variants. */
+ priority = get_builtin_code_for_version (version_decl,
+ &predicate_chain);
+
+ if (predicate_chain == NULL_TREE)
+ continue;
+
+ actual_versions++;
+ function_version_info [ix - 1].version_decl = version_decl;
+ function_version_info [ix - 1].predicate_chain = predicate_chain;
+ function_version_info [ix - 1].dispatch_priority = priority;
+ }
+
+ /* Sort the versions according to descending order of dispatch priority. The
+ priority is based on the ISA. This is not a perfect solution. There
+ could still be ambiguity. If more than one function version is suitable
+ to execute, which one should be dispatched? In future, allow the user
+ to specify a dispatch priority next to the version. */
+ qsort (function_version_info, actual_versions,
+ sizeof (struct _function_version_info), feature_compare);
+
+ for (i = 0; i < actual_versions; ++i)
+ *empty_bb = add_condition_to_bb (dispatch_decl,
+ function_version_info[i].version_decl,
+ function_version_info[i].predicate_chain,
+ *empty_bb);
+
+ /* dispatch default version at the end. */
+ *empty_bb = add_condition_to_bb (dispatch_decl, default_decl,
+ NULL, *empty_bb);
+
+ free (function_version_info);
+ return 0;
+}
+
+/* Comparator function to be used in qsort routine to sort attribute
+ specification strings to "target". */
+
+static int
+attr_strcmp (const void *v1, const void *v2)
+{
+ const char *c1 = *(char *const*)v1;
+ const char *c2 = *(char *const*)v2;
+ return strcmp (c1, c2);
+}
+
+/* ARGLIST is the argument to target attribute. This function tokenizes
+ the comma separated arguments, sorts them and returns a string which
+ is a unique identifier for the comma separated arguments. It also
+ replaces non-identifier characters "=,-" with "_". */
+
+static char *
+sorted_attr_string (tree arglist)
+{
+ tree arg;
+ size_t str_len_sum = 0;
+ char **args = NULL;
+ char *attr_str, *ret_str;
+ char *attr = NULL;
+ unsigned int argnum = 1;
+ unsigned int i;
+
+ for (arg = arglist; arg; arg = TREE_CHAIN (arg))
+ {
+ const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
+ size_t len = strlen (str);
+ str_len_sum += len + 1;
+ if (arg != arglist)
+ argnum++;
+ for (i = 0; i < strlen (str); i++)
+ if (str[i] == ',')
+ argnum++;
+ }
+
+ attr_str = XNEWVEC (char, str_len_sum);
+ str_len_sum = 0;
+ for (arg = arglist; arg; arg = TREE_CHAIN (arg))
+ {
+ const char *str = TREE_STRING_POINTER (TREE_VALUE (arg));
+ size_t len = strlen (str);
+ memcpy (attr_str + str_len_sum, str, len);
+ attr_str[str_len_sum + len] = TREE_CHAIN (arg) ? ',' : '\0';
+ str_len_sum += len + 1;
+ }
+
+ /* Replace "=,-" with "_". */
+ for (i = 0; i < strlen (attr_str); i++)
+ if (attr_str[i] == '=' || attr_str[i]== '-')
+ attr_str[i] = '_';
+
+ if (argnum == 1)
+ return attr_str;
+
+ args = XNEWVEC (char *, argnum);
+
+ i = 0;
+ attr = strtok (attr_str, ",");
+ while (attr != NULL)
+ {
+ args[i] = attr;
+ i++;
+ attr = strtok (NULL, ",");
+ }
+
+ qsort (args, argnum, sizeof (char *), attr_strcmp);
+
+ ret_str = XNEWVEC (char, str_len_sum);
+ str_len_sum = 0;
+ for (i = 0; i < argnum; i++)
+ {
+ size_t len = strlen (args[i]);
+ memcpy (ret_str + str_len_sum, args[i], len);
+ ret_str[str_len_sum + len] = i < argnum - 1 ? '_' : '\0';
+ str_len_sum += len + 1;
+ }
+
+ XDELETEVEC (args);
+ XDELETEVEC (attr_str);
+ return ret_str;
+}
+
+/* This function changes the assembler name for functions that are
+ versions. If DECL is a function version and has a "target"
+ attribute, it appends the attribute string to its assembler name. */
+
+static tree
+ix86_mangle_function_version_assembler_name (tree decl, tree id)
+{
+ tree version_attr;
+ const char *orig_name, *version_string;
+ char *attr_str, *assembler_name;
+
+ if (DECL_DECLARED_INLINE_P (decl)
+ && lookup_attribute ("gnu_inline",
+ DECL_ATTRIBUTES (decl)))
+ error_at (DECL_SOURCE_LOCATION (decl),
+ "Function versions cannot be marked as gnu_inline,"
+ " bodies have to be generated");
+
+ if (DECL_VIRTUAL_P (decl)
+ || DECL_VINDEX (decl))
+ sorry ("Virtual function multiversioning not supported");
+
+ version_attr = lookup_attribute ("target", DECL_ATTRIBUTES (decl));
+
+ /* target attribute string cannot be NULL. */
+ gcc_assert (version_attr != NULL_TREE);
+
+ orig_name = IDENTIFIER_POINTER (id);
+ version_string
+ = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (version_attr)));
+
+ if (strcmp (version_string, "default") == 0)
+ return id;
+
+ attr_str = sorted_attr_string (TREE_VALUE (version_attr));
+ assembler_name = XNEWVEC (char, strlen (orig_name) + strlen (attr_str) + 2);
+
+ sprintf (assembler_name, "%s.%s", orig_name, attr_str);
+
+ /* Allow assembler name to be modified if already set. */
+ if (DECL_ASSEMBLER_NAME_SET_P (decl))
+ SET_DECL_RTL (decl, NULL);
+
+ tree ret = get_identifier (assembler_name);
+ XDELETEVEC (attr_str);
+ XDELETEVEC (assembler_name);
+ return ret;
+}
+
+/* This function returns true if FN1 and FN2 are versions of the same function,
+ that is, the target strings of the function decls are different. This assumes
+ that FN1 and FN2 have the same signature. */
+
+static bool
+ix86_function_versions (tree fn1, tree fn2)
+{
+ tree attr1, attr2;
+ char *target1, *target2;
+ bool result;
+
+ if (TREE_CODE (fn1) != FUNCTION_DECL
+ || TREE_CODE (fn2) != FUNCTION_DECL)
+ return false;
+
+ attr1 = lookup_attribute ("target", DECL_ATTRIBUTES (fn1));
+ attr2 = lookup_attribute ("target", DECL_ATTRIBUTES (fn2));
+
+ /* At least one function decl should have the target attribute specified. */
+ if (attr1 == NULL_TREE && attr2 == NULL_TREE)
+ return false;
+
+ /* Diagnose missing target attribute if one of the decls is already
+ multi-versioned. */
+ if (attr1 == NULL_TREE || attr2 == NULL_TREE)
+ {
+ if (DECL_FUNCTION_VERSIONED (fn1) || DECL_FUNCTION_VERSIONED (fn2))
+ {
+ if (attr2 != NULL_TREE)
+ {
+ tree tem = fn1;
+ fn1 = fn2;
+ fn2 = tem;
+ attr1 = attr2;
+ }
+ error_at (DECL_SOURCE_LOCATION (fn2),
+ "missing %<target%> attribute for multi-versioned %D",
+ fn2);
+ error_at (DECL_SOURCE_LOCATION (fn1),
+ "previous declaration of %D", fn1);
+ /* Prevent diagnosing of the same error multiple times. */
+ DECL_ATTRIBUTES (fn2)
+ = tree_cons (get_identifier ("target"),
+ copy_node (TREE_VALUE (attr1)),
+ DECL_ATTRIBUTES (fn2));
+ }
+ return false;
+ }
+
+ target1 = sorted_attr_string (TREE_VALUE (attr1));
+ target2 = sorted_attr_string (TREE_VALUE (attr2));
+
+ /* The sorted target strings must be different for fn1 and fn2
+ to be versions. */
+ if (strcmp (target1, target2) == 0)
+ result = false;
+ else
+ result = true;
+
+ XDELETEVEC (target1);
+ XDELETEVEC (target2);
+
+ return result;
+}
+
+static tree
+ix86_mangle_decl_assembler_name (tree decl, tree id)
+{
+ /* For function version, add the target suffix to the assembler name. */
+ if (TREE_CODE (decl) == FUNCTION_DECL
+ && DECL_FUNCTION_VERSIONED (decl))
+ return ix86_mangle_function_version_assembler_name (decl, id);
+
+ return id;
+}
+
+/* Return a new name by appending SUFFIX to the DECL name. If make_unique
+ is true, append the full path name of the source file. */
+
+static char *
+make_name (tree decl, const char *suffix, bool make_unique)
+{
+ char *global_var_name;
+ int name_len;
+ const char *name;
+ const char *unique_name = NULL;
+
+ name = IDENTIFIER_POINTER (DECL_ASSEMBLER_NAME (decl));
+
+ /* Get a unique name that can be used globally without any chances
+ of collision at link time. */
+ if (make_unique)
+ unique_name = IDENTIFIER_POINTER (get_file_function_name ("\0"));
+
+ name_len = strlen (name) + strlen (suffix) + 2;
+
+ if (make_unique)
+ name_len += strlen (unique_name) + 1;
+ global_var_name = XNEWVEC (char, name_len);
+
+ /* Use '.' to concatenate names as it is demangler friendly. */
+ if (make_unique)
+ snprintf (global_var_name, name_len, "%s.%s.%s", name, unique_name,
+ suffix);
+ else
+ snprintf (global_var_name, name_len, "%s.%s", name, suffix);
+
+ return global_var_name;
+}
+
+/* Make a dispatcher declaration for the multi-versioned function DECL.
+ Calls to DECL function will be replaced with calls to the dispatcher
+ by the front-end. Return the decl created. */
+
+static tree
+make_dispatcher_decl (const tree decl)
+{
+ tree func_decl;
+ char *func_name;
+ tree fn_type, func_type;
+ bool is_uniq = false;
+
+ if (TREE_PUBLIC (decl) == 0)
+ is_uniq = true;
+
+ func_name = make_name (decl, "ifunc", is_uniq);
+
+ fn_type = TREE_TYPE (decl);
+ func_type = build_function_type (TREE_TYPE (fn_type),
+ TYPE_ARG_TYPES (fn_type));
+
+ func_decl = build_fn_decl (func_name, func_type);
+ XDELETEVEC (func_name);
+ TREE_USED (func_decl) = 1;
+ DECL_CONTEXT (func_decl) = NULL_TREE;
+ DECL_INITIAL (func_decl) = error_mark_node;
+ DECL_ARTIFICIAL (func_decl) = 1;
+ /* Mark this func as external, the resolver will flip it again if
+ it gets generated. */
+ DECL_EXTERNAL (func_decl) = 1;
+ /* This will be of type IFUNCs have to be externally visible. */
+ TREE_PUBLIC (func_decl) = 1;
+
+ return func_decl;
+}
+
+/* Returns true if decl is multi-versioned and DECL is the default function,
+ that is it is not tagged with target specific optimization. */
+
+static bool
+is_function_default_version (const tree decl)
+{
+ if (TREE_CODE (decl) != FUNCTION_DECL
+ || !DECL_FUNCTION_VERSIONED (decl))
+ return false;
+ tree attr = lookup_attribute ("target", DECL_ATTRIBUTES (decl));
+ gcc_assert (attr);
+ attr = TREE_VALUE (TREE_VALUE (attr));
+ return (TREE_CODE (attr) == STRING_CST
+ && strcmp (TREE_STRING_POINTER (attr), "default") == 0);
+}
+
+/* Make a dispatcher declaration for the multi-versioned function DECL.
+ Calls to DECL function will be replaced with calls to the dispatcher
+ by the front-end. Returns the decl of the dispatcher function. */
+
+static tree
+ix86_get_function_versions_dispatcher (void *decl)
+{
+ tree fn = (tree) decl;
+ struct cgraph_node *node = NULL;
+ struct cgraph_node *default_node = NULL;
+ struct cgraph_function_version_info *node_v = NULL;
+ struct cgraph_function_version_info *it_v = NULL;
+ struct cgraph_function_version_info *first_v = NULL;
+
+ tree dispatch_decl = NULL;
+ struct cgraph_node *dispatcher_node = NULL;
+ struct cgraph_function_version_info *dispatcher_version_info = NULL;
+
+ struct cgraph_function_version_info *default_version_info = NULL;
+
+ gcc_assert (fn != NULL && DECL_FUNCTION_VERSIONED (fn));
+
+ node = cgraph_get_node (fn);
+ gcc_assert (node != NULL);
+
+ node_v = get_cgraph_node_version (node);
+ gcc_assert (node_v != NULL);
+
+ if (node_v->dispatcher_resolver != NULL)
+ return node_v->dispatcher_resolver;
+
+ /* Find the default version and make it the first node. */
+ first_v = node_v;
+ /* Go to the beginnig of the chain. */
+ while (first_v->prev != NULL)
+ first_v = first_v->prev;
+ default_version_info = first_v;
+ while (default_version_info != NULL)
+ {
+ if (is_function_default_version
+ (default_version_info->this_node->decl))
+ break;
+ default_version_info = default_version_info->next;
+ }
+
+ /* If there is no default node, just return NULL. */
+ if (default_version_info == NULL)
+ return NULL;
+
+ /* Make default info the first node. */
+ if (first_v != default_version_info)
+ {
+ default_version_info->prev->next = default_version_info->next;
+ if (default_version_info->next)
+ default_version_info->next->prev = default_version_info->prev;
+ first_v->prev = default_version_info;
+ default_version_info->next = first_v;
+ default_version_info->prev = NULL;
+ }
+
+ default_node = default_version_info->this_node;
+
+#if defined (ASM_OUTPUT_TYPE_DIRECTIVE) && HAVE_GNU_INDIRECT_FUNCTION
+ /* Right now, the dispatching is done via ifunc. */
+ dispatch_decl = make_dispatcher_decl (default_node->decl);
+
+ dispatcher_node = cgraph_get_create_node (dispatch_decl);
+ gcc_assert (dispatcher_node != NULL);
+ dispatcher_node->dispatcher_function = 1;
+ dispatcher_version_info
+ = insert_new_cgraph_node_version (dispatcher_node);
+ dispatcher_version_info->next = default_version_info;
+ dispatcher_node->local.finalized = 1;
+
+ /* Set the dispatcher for all the versions. */
+ it_v = default_version_info;
+ while (it_v != NULL)
+ {
+ it_v->dispatcher_resolver = dispatch_decl;
+ it_v = it_v->next;
+ }
+#else
+ error_at (DECL_SOURCE_LOCATION (default_node->symbol.decl),
+ "multiversioning needs ifunc which is not supported "
+ "in this configuration");
+#endif
+ return dispatch_decl;
+}
+
+/* Makes a function attribute of the form NAME(ARG_NAME) and chains
+ it to CHAIN. */
+
+static tree
+make_attribute (const char *name, const char *arg_name, tree chain)
+{
+ tree attr_name;
+ tree attr_arg_name;
+ tree attr_args;
+ tree attr;
+
+ attr_name = get_identifier (name);
+ attr_arg_name = build_string (strlen (arg_name), arg_name);
+ attr_args = tree_cons (NULL_TREE, attr_arg_name, NULL_TREE);
+ attr = tree_cons (attr_name, attr_args, chain);
+ return attr;
+}
+
+/* Make the resolver function decl to dispatch the versions of
+ a multi-versioned function, DEFAULT_DECL. Create an
+ empty basic block in the resolver and store the pointer in
+ EMPTY_BB. Return the decl of the resolver function. */
+
+static tree
+make_resolver_func (const tree default_decl,
+ const tree dispatch_decl,
+ basic_block *empty_bb)
+{
+ char *resolver_name;
+ tree decl, type, decl_name, t;
+ bool is_uniq = false;
+
+ /* IFUNC's have to be globally visible. So, if the default_decl is
+ not, then the name of the IFUNC should be made unique. */
+ if (TREE_PUBLIC (default_decl) == 0)
+ is_uniq = true;
+
+ /* Append the filename to the resolver function if the versions are
+ not externally visible. This is because the resolver function has
+ to be externally visible for the loader to find it. So, appending
+ the filename will prevent conflicts with a resolver function from
+ another module which is based on the same version name. */
+ resolver_name = make_name (default_decl, "resolver", is_uniq);
+
+ /* The resolver function should return a (void *). */
+ type = build_function_type_list (ptr_type_node, NULL_TREE);
+
+ decl = build_fn_decl (resolver_name, type);
+ decl_name = get_identifier (resolver_name);
+ SET_DECL_ASSEMBLER_NAME (decl, decl_name);
+
+ DECL_NAME (decl) = decl_name;
+ TREE_USED (decl) = 1;
+ DECL_ARTIFICIAL (decl) = 1;
+ DECL_IGNORED_P (decl) = 0;
+ /* IFUNC resolvers have to be externally visible. */
+ TREE_PUBLIC (decl) = 1;
+ DECL_UNINLINABLE (decl) = 0;
+
+ /* Resolver is not external, body is generated. */
+ DECL_EXTERNAL (decl) = 0;
+ DECL_EXTERNAL (dispatch_decl) = 0;
+
+ DECL_CONTEXT (decl) = NULL_TREE;
+ DECL_INITIAL (decl) = make_node (BLOCK);
+ DECL_STATIC_CONSTRUCTOR (decl) = 0;
+
+ if (DECL_COMDAT_GROUP (default_decl)
+ || TREE_PUBLIC (default_decl))
+ {
+ /* In this case, each translation unit with a call to this
+ versioned function will put out a resolver. Ensure it
+ is comdat to keep just one copy. */
+ DECL_COMDAT (decl) = 1;
+ make_decl_one_only (decl, DECL_ASSEMBLER_NAME (decl));
+ }
+ /* Build result decl and add to function_decl. */
+ t = build_decl (UNKNOWN_LOCATION, RESULT_DECL, NULL_TREE, ptr_type_node);
+ DECL_ARTIFICIAL (t) = 1;
+ DECL_IGNORED_P (t) = 1;
+ DECL_RESULT (decl) = t;
+
+ gimplify_function_tree (decl);
+ push_cfun (DECL_STRUCT_FUNCTION (decl));
+ *empty_bb = init_lowered_empty_function (decl, false);
+
+ cgraph_add_new_function (decl, true);
+ cgraph_call_function_insertion_hooks (cgraph_get_create_node (decl));
+
+ pop_cfun ();
+
+ gcc_assert (dispatch_decl != NULL);
+ /* Mark dispatch_decl as "ifunc" with resolver as resolver_name. */
+ DECL_ATTRIBUTES (dispatch_decl)
+ = make_attribute ("ifunc", resolver_name, DECL_ATTRIBUTES (dispatch_decl));
+
+ /* Create the alias for dispatch to resolver here. */
+ /*cgraph_create_function_alias (dispatch_decl, decl);*/
+ cgraph_same_body_alias (NULL, dispatch_decl, decl);
+ XDELETEVEC (resolver_name);
+ return decl;
+}
+
+/* Generate the dispatching code body to dispatch multi-versioned function
+ DECL. The target hook is called to process the "target" attributes and
+ provide the code to dispatch the right function at run-time. NODE points
+ to the dispatcher decl whose body will be created. */
+
+static tree
+ix86_generate_version_dispatcher_body (void *node_p)
+{
+ tree resolver_decl;
+ basic_block empty_bb;
+ VEC (tree, heap) *fn_ver_vec = NULL;
+ tree default_ver_decl;
+ struct cgraph_node *versn;
+ struct cgraph_node *node;
+
+ struct cgraph_function_version_info *node_version_info = NULL;
+ struct cgraph_function_version_info *versn_info = NULL;
+
+ node = (struct cgraph_node *)node_p;
+
+ node_version_info = get_cgraph_node_version (node);
+ gcc_assert (node->dispatcher_function
+ && node_version_info != NULL);
+
+ if (node_version_info->dispatcher_resolver)
+ return node_version_info->dispatcher_resolver;
+
+ /* The first version in the chain corresponds to the default version. */
+ default_ver_decl = node_version_info->next->this_node->decl;
+
+ /* node is going to be an alias, so remove the finalized bit. */
+ node->local.finalized = false;
+
+ resolver_decl = make_resolver_func (default_ver_decl,
+ node->decl, &empty_bb);
+
+ node_version_info->dispatcher_resolver = resolver_decl;
+
+ push_cfun (DECL_STRUCT_FUNCTION (resolver_decl));
+
+ fn_ver_vec = VEC_alloc (tree, heap, 2);
+
+ for (versn_info = node_version_info->next; versn_info;
+ versn_info = versn_info->next)
+ {
+ versn = versn_info->this_node;
+ /* Check for virtual functions here again, as by this time it should
+ have been determined if this function needs a vtable index or
+ not. This happens for methods in derived classes that override
+ virtual methods in base classes but are not explicitly marked as
+ virtual. */
+ if (DECL_VINDEX (versn->decl))
+ sorry ("Virtual function multiversioning not supported");
+
+ VEC_safe_push (tree, heap, fn_ver_vec, versn->decl);
+ }
+
+ dispatch_function_versions (resolver_decl, fn_ver_vec, &empty_bb);
+ VEC_free (tree, heap, fn_ver_vec);
+ rebuild_cgraph_edges ();
+ pop_cfun ();
+ return resolver_decl;
+}
+
/* This builds the processor_model struct type defined in
libgcc/config/i386/cpuinfo.c */
@@ -28139,16 +29163,10 @@ fold_builtin_cpu (tree fndecl, tree *args)
{"avx2", F_AVX2}
};
- static tree __processor_model_type = NULL_TREE;
- static tree __cpu_model_var = NULL_TREE;
+ tree __processor_model_type = build_processor_model_struct ();
+ tree __cpu_model_var = make_var_decl (__processor_model_type,
+ "__cpu_model");
- if (__processor_model_type == NULL_TREE)
- __processor_model_type = build_processor_model_struct ();
-
- if (__cpu_model_var == NULL_TREE)
- __cpu_model_var = make_var_decl (__processor_model_type,
- "__cpu_model");
-
gcc_assert ((args != NULL) && (*args != NULL));
param_string_cst = *args;
@@ -28171,6 +29189,8 @@ fold_builtin_cpu (tree fndecl, tree *args)
{
tree ref;
tree field;
+ tree final;
+
unsigned int field_val = 0;
unsigned int NUM_ARCH_NAMES
= sizeof (arch_names_table) / sizeof (struct _arch_names_table);
@@ -28210,14 +29230,17 @@ fold_builtin_cpu (tree fndecl, tree *args)
field, NULL_TREE);
/* Check the value. */
- return build2 (EQ_EXPR, unsigned_type_node, ref,
- build_int_cstu (unsigned_type_node, field_val));
+ final = build2 (EQ_EXPR, unsigned_type_node, ref,
+ build_int_cstu (unsigned_type_node, field_val));
+ return build1 (CONVERT_EXPR, integer_type_node, final);
}
else if (fn_code == IX86_BUILTIN_CPU_SUPPORTS)
{
tree ref;
tree array_elt;
tree field;
+ tree final;
+
unsigned int field_val = 0;
unsigned int NUM_ISA_NAMES
= sizeof (isa_names_table) / sizeof (struct _isa_names_table);
@@ -28249,8 +29272,9 @@ fold_builtin_cpu (tree fndecl, tree *args)
field_val = (1 << isa_names_table[i].feature);
/* Return __cpu_model.__cpu_features[0] & field_val */
- return build2 (BIT_AND_EXPR, unsigned_type_node, array_elt,
- build_int_cstu (unsigned_type_node, field_val));
+ final = build2 (BIT_AND_EXPR, unsigned_type_node, array_elt,
+ build_int_cstu (unsigned_type_node, field_val));
+ return build1 (CONVERT_EXPR, integer_type_node, final);
}
gcc_unreachable ();
}
@@ -39434,6 +40458,9 @@ ix86_autovectorize_vector_sizes (void)
#undef TARGET_PROFILE_BEFORE_PROLOGUE
#define TARGET_PROFILE_BEFORE_PROLOGUE ix86_profile_before_prologue
+#undef TARGET_MANGLE_DECL_ASSEMBLER_NAME
+#define TARGET_MANGLE_DECL_ASSEMBLER_NAME ix86_mangle_decl_assembler_name
+
#undef TARGET_ASM_UNALIGNED_HI_OP
#define TARGET_ASM_UNALIGNED_HI_OP TARGET_ASM_ALIGNED_HI_OP
#undef TARGET_ASM_UNALIGNED_SI_OP
@@ -39527,6 +40554,17 @@ ix86_autovectorize_vector_sizes (void)
#undef TARGET_FOLD_BUILTIN
#define TARGET_FOLD_BUILTIN ix86_fold_builtin
+#undef TARGET_COMPARE_VERSION_PRIORITY
+#define TARGET_COMPARE_VERSION_PRIORITY ix86_compare_version_priority
+
+#undef TARGET_GENERATE_VERSION_DISPATCHER_BODY
+#define TARGET_GENERATE_VERSION_DISPATCHER_BODY \
+ ix86_generate_version_dispatcher_body
+
+#undef TARGET_GET_FUNCTION_VERSIONS_DISPATCHER
+#define TARGET_GET_FUNCTION_VERSIONS_DISPATCHER \
+ ix86_get_function_versions_dispatcher
+
#undef TARGET_ENUM_VA_LIST_P
#define TARGET_ENUM_VA_LIST_P ix86_enum_va_list
@@ -39656,6 +40694,9 @@ ix86_autovectorize_vector_sizes (void)
#undef TARGET_OPTION_PRINT
#define TARGET_OPTION_PRINT ix86_function_specific_print
+#undef TARGET_OPTION_FUNCTION_VERSIONS
+#define TARGET_OPTION_FUNCTION_VERSIONS ix86_function_versions
+
#undef TARGET_CAN_INLINE_P
#define TARGET_CAN_INLINE_P ix86_can_inline_p
===================================================================
@@ -931,6 +931,21 @@ cgraph_analyze_function (struct cgraph_node *node)
cgraph_create_edge (node, cgraph_get_node (node->thunk.alias),
NULL, 0, CGRAPH_FREQ_BASE);
}
+ else if (node->dispatcher_function)
+ {
+ /* Generate the dispatcher body of multi-versioned functions. */
+ struct cgraph_function_version_info *dispatcher_version_info
+ = get_cgraph_node_version (node);
+ if (dispatcher_version_info != NULL
+ && (dispatcher_version_info->dispatcher_resolver
+ == NULL_TREE))
+ {
+ tree resolver = NULL_TREE;
+ gcc_assert (targetm.generate_version_dispatcher_body);
+ resolver = targetm.generate_version_dispatcher_body (node);
+ gcc_assert (resolver != NULL_TREE);
+ }
+ }
else
{
current_function_decl = decl;
@@ -1153,7 +1168,8 @@ cgraph_analyze_functions (void)
gcc.c-torture/compile/20011119-1.c */
if (!DECL_STRUCT_FUNCTION (decl)
&& (!node->alias || !node->thunk.alias)
- && !node->thunk.thunk_p)
+ && !node->thunk.thunk_p
+ && !node->dispatcher_function)
{
cgraph_reset_node (node);
node->local.redefined_extern_inline = true;
@@ -1642,13 +1658,13 @@ cgraph_mark_functions_to_output (void)
}
/* DECL is FUNCTION_DECL. Initialize datastructures so DECL is a function
- in lowered gimple form.
+ in lowered gimple form. IN_SSA is true if the gimple is in SSA.
Set current_function_decl and cfun to newly constructed empty function body.
return basic block in the function body. */
-static basic_block
-init_lowered_empty_function (tree decl)
+basic_block
+init_lowered_empty_function (tree decl, bool in_ssa)
{
basic_block bb;
@@ -1656,9 +1672,14 @@ cgraph_mark_functions_to_output (void)
allocate_struct_function (decl, false);
gimple_register_cfg_hooks ();
init_empty_tree_cfg ();
- init_tree_ssa (cfun);
- init_ssa_operands ();
- cfun->gimple_df->in_ssa_p = true;
+
+ if (in_ssa)
+ {
+ init_tree_ssa (cfun);
+ init_ssa_operands ();
+ cfun->gimple_df->in_ssa_p = true;
+ }
+
DECL_INITIAL (decl) = make_node (BLOCK);
DECL_SAVED_TREE (decl) = error_mark_node;
@@ -1876,7 +1897,7 @@ assemble_thunk (struct cgraph_node *node)
else
resdecl = DECL_RESULT (thunk_fndecl);
- bb = then_bb = else_bb = return_bb = init_lowered_empty_function (thunk_fndecl);
+ bb = then_bb = else_bb = return_bb = init_lowered_empty_function (thunk_fndecl, true);
bsi = gsi_start_bb (bb);
===================================================================
@@ -3586,6 +3586,11 @@ Enable/disable the generation of the advanced bit
@cindex @code{target("aes")} attribute
Enable/disable the generation of the AES instructions.
+@item default
+@cindex @code{target("default")} attribute
+@xref{Function Multiversioning}, where it is used to specify the
+default function version.
+
@item mmx
@itemx no-mmx
@cindex @code{target("mmx")} attribute
@@ -15032,6 +15037,7 @@ Predefined Macros,cpp,The GNU C Preprocessor}).
* Bound member functions:: You can extract a function pointer to the
method denoted by a @samp{->*} or @samp{.*} expression.
* C++ Attributes:: Variable, function, and type attributes for C++ only.
+* Function Multiversioning:: Declaring multiple function versions.
* Namespace Association:: Strong using-directives for namespace association.
* Type Traits:: Compiler support for type traits
* Java Exceptions:: Tweaking exception handling to work with Java.
@@ -15547,6 +15553,64 @@ interface table mechanism, instead of regular virt
See also @ref{Namespace Association}.
+@node Function Multiversioning
+@section Function Multiversioning
+@cindex function versions
+
+With the GNU C++ front end, for target i386, you may specify multiple
+versions of a function, where each function is specialized for a
+specific target feature. At runtime, the appropriate version of the
+function is automatically executed depending on the characteristics of
+the execution platform. Here is an example.
+
+@smallexample
+__attribute__ ((target ("default")))
+int foo ()
+@{
+ // The default version of foo.
+ return 0;
+@}
+
+__attribute__ ((target ("sse4.2")))
+int foo ()
+@{
+ // foo version for SSE4.2
+ return 1;
+@}
+
+__attribute__ ((target ("arch=atom")))
+int foo ()
+@{
+ // foo version for the Intel ATOM processor
+ return 2;
+@}
+
+__attribute__ ((target ("arch=amdfam10")))
+int foo ()
+@{
+ // foo version for the AMD Family 0x10 processors.
+ return 3;
+@}
+
+int main ()
+@{
+ int (*p)() = &foo;
+ assert ((*p) () == foo ());
+ return 0;
+@}
+@end smallexample
+
+In the above example, four versions of function foo are created. The
+first version of foo with the target attribute "default" is the default
+version. This version gets executed when no other target specific
+version qualifies for execution on a particular platform. A new version
+of foo is created by using the same function signature but with a
+different target string. Function foo is called or a pointer to it is
+taken just like a regular function. GCC takes care of doing the
+dispatching to call the right version at runtime. Refer to the
+@uref{http://gcc.gnu.org/wiki/FunctionMultiVersioning, GCC wiki on
+Function Multiversioning} for more details.
+
@node Namespace Association
@section Namespace Association
===================================================================
@@ -9938,6 +9938,14 @@ changed via the optimize attribute or pragma, see
@code{TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE}
@end deftypefn
+@deftypefn {Target Hook} bool TARGET_OPTION_FUNCTION_VERSIONS (tree @var{decl1}, tree @var{decl2})
+This target hook returns @code{true} if @var{DECL1} and @var{DECL2} are
+versions of the same function. @var{DECL1} and @var{DECL2} are function
+versions if and only if they have the same function signature and
+different target specific attributes, that is, they are compiled for
+different target machines.
+@end deftypefn
+
@deftypefn {Target Hook} bool TARGET_CAN_INLINE_P (tree @var{caller}, tree @var{callee})
This target hook returns @code{false} if the @var{caller} function
cannot inline @var{callee}, based on target specific information. By
@@ -11008,6 +11016,29 @@ Return true if unaligned vector memory load/store
on this target.
@end deftypefn
+@deftypefn {Target Hook} int TARGET_COMPARE_VERSION_PRIORITY (tree @var{decl1}, tree @var{decl2})
+This hook is used to compare the target attributes in two functions to
+determine which function's features get higher priority. This is used
+during function multi-versioning to figure out the order in which two
+versions must be dispatched. A function version with a higher priority
+is checked for dispatching earlier. @var{decl1} and @var{decl2} are
+ the two function decls that will be compared.
+@end deftypefn
+
+@deftypefn {Target Hook} tree TARGET_GET_FUNCTION_VERSIONS_DISPATCHER (void *@var{decl})
+This hook is used to get the dispatcher function for a set of function
+versions. The dispatcher function is called to invoke the right function
+version at run-time. @var{decl} is one version from a set of semantically
+identical versions.
+@end deftypefn
+
+@deftypefn {Target Hook} tree TARGET_GENERATE_VERSION_DISPATCHER_BODY (void *@var{arg})
+This hook is used to generate the dispatcher logic to invoke the right
+function version at run-time for a given set of function versions.
+@var{arg} points to the callgraph node of the dispatcher function whose
+body must be generated.
+@end deftypefn
+
@deftypefn {Target Hook} {const char *} TARGET_INVALID_WITHIN_DOLOOP (const_rtx @var{insn})
Take an instruction in @var{insn} and return NULL if it is valid within a
===================================================================
@@ -9825,6 +9825,14 @@ changed via the optimize attribute or pragma, see
@code{TARGET_OVERRIDE_OPTIONS_AFTER_CHANGE}
@end deftypefn
+@hook TARGET_OPTION_FUNCTION_VERSIONS
+This target hook returns @code{true} if @var{DECL1} and @var{DECL2} are
+versions of the same function. @var{DECL1} and @var{DECL2} are function
+versions if and only if they have the same function signature and
+different target specific attributes, that is, they are compiled for
+different target machines.
+@end deftypefn
+
@hook TARGET_CAN_INLINE_P
This target hook returns @code{false} if the @var{caller} function
cannot inline @var{callee}, based on target specific information. By
@@ -10880,6 +10888,29 @@ Return true if unaligned vector memory load/store
on this target.
@end deftypefn
+@hook TARGET_COMPARE_VERSION_PRIORITY
+This hook is used to compare the target attributes in two functions to
+determine which function's features get higher priority. This is used
+during function multi-versioning to figure out the order in which two
+versions must be dispatched. A function version with a higher priority
+is checked for dispatching earlier. @var{decl1} and @var{decl2} are
+ the two function decls that will be compared.
+@end deftypefn
+
+@hook TARGET_GET_FUNCTION_VERSIONS_DISPATCHER
+This hook is used to get the dispatcher function for a set of function
+versions. The dispatcher function is called to invoke the right function
+version at run-time. @var{decl} is one version from a set of semantically
+identical versions.
+@end deftypefn
+
+@hook TARGET_GENERATE_VERSION_DISPATCHER_BODY
+This hook is used to generate the dispatcher logic to invoke the right
+function version at run-time for a given set of function versions.
+@var{arg} points to the callgraph node of the dispatcher function whose
+body must be generated.
+@end deftypefn
+
@hook TARGET_INVALID_WITHIN_DOLOOP
Take an instruction in @var{insn} and return NULL if it is valid within a
===================================================================
@@ -3552,6 +3552,12 @@ extern VEC(tree, gc) **decl_debug_args_insert (tre
#define DECL_FUNCTION_SPECIFIC_OPTIMIZATION(NODE) \
(FUNCTION_DECL_CHECK (NODE)->function_decl.function_specific_optimization)
+/* In FUNCTION_DECL, this is set if this function has other versions generated
+ using "target" attributes. The default version is the one which does not
+ have any "target" attribute set. */
+#define DECL_FUNCTION_VERSIONED(NODE)\
+ (FUNCTION_DECL_CHECK (NODE)->function_decl.versioned_function)
+
/* FUNCTION_DECL inherits from DECL_NON_COMMON because of the use of the
arguments/result/saved_tree fields by front ends. It was either inherit
FUNCTION_DECL from non_common, or inherit non_common from FUNCTION_DECL,
@@ -3596,8 +3602,8 @@ struct GTY(()) tree_function_decl {
unsigned looping_const_or_pure_flag : 1;
unsigned has_debug_args_flag : 1;
unsigned tm_clone_flag : 1;
-
- /* 1 bit left */
+ unsigned versioned_function : 1;
+ /* No bits left. */
};
/* The source language of the translation-unit. */
Property changes on: gcc/testsuite/gcc.target/powerpc/ppc-round.c
___________________________________________________________________
Modified: svn:mergeinfo
Merged /trunk/gcc/testsuite/gcc.target/powerpc/ppc-round.c:r193204,193486,193555,194730,194818,194828,195584,195967,196033
===================================================================
@@ -4,7 +4,7 @@
// { dg-do run { target i?86-*-* x86_64-*-* } }
// { dg-require-ifunc "" }
// { dg-options "-O2" }
-// { dg-additional-sources "mv12-aux.C" }
+// { dg-additional-sources "mv12-aux.cc" }
#include "mv12.h"
===================================================================
@@ -1,6 +1,4 @@
-// Header file used by mv12.C and mv12-aux.C.
-// { dg-do compile { target i?86-*-* x86_64-*-* } }
-// { dg-options "" }
+// Header file used by mv12.C and mv12-aux.cc.
int foo () __attribute__ ((target ("default")));
int foo () __attribute__ ((target ("sse4.2")));
===================================================================
@@ -215,6 +215,144 @@ static GTY(()) struct cgraph_edge *free_edges;
/* Did procss_same_body_aliases run? */
bool same_body_aliases_done;
+/* Map a cgraph_node to cgraph_function_version_info using this htab.
+ The cgraph_function_version_info has a THIS_NODE field that is the
+ corresponding cgraph_node.. */
+
+static htab_t GTY((param_is (struct cgraph_function_version_info *)))
+ cgraph_fnver_htab = NULL;
+
+/* Hash function for cgraph_fnver_htab. */
+static hashval_t
+cgraph_fnver_htab_hash (const void *ptr)
+{
+ int uid = ((const struct cgraph_function_version_info *)ptr)->this_node->uid;
+ return (hashval_t)(uid);
+}
+
+/* eq function for cgraph_fnver_htab. */
+static int
+cgraph_fnver_htab_eq (const void *p1, const void *p2)
+{
+ const struct cgraph_function_version_info *n1
+ = (const struct cgraph_function_version_info *)p1;
+ const struct cgraph_function_version_info *n2
+ = (const struct cgraph_function_version_info *)p2;
+
+ return n1->this_node->uid == n2->this_node->uid;
+}
+
+/* Mark as GC root all allocated nodes. */
+static GTY(()) struct cgraph_function_version_info *
+ version_info_node = NULL;
+
+/* Get the cgraph_function_version_info node corresponding to node. */
+struct cgraph_function_version_info *
+get_cgraph_node_version (struct cgraph_node *node)
+{
+ struct cgraph_function_version_info *ret;
+ struct cgraph_function_version_info key;
+ key.this_node = node;
+
+ if (cgraph_fnver_htab == NULL)
+ return NULL;
+
+ ret = (struct cgraph_function_version_info *)
+ htab_find (cgraph_fnver_htab, &key);
+
+ return ret;
+}
+
+/* Insert a new cgraph_function_version_info node into cgraph_fnver_htab
+ corresponding to cgraph_node NODE. */
+struct cgraph_function_version_info *
+insert_new_cgraph_node_version (struct cgraph_node *node)
+{
+ void **slot;
+
+ version_info_node = NULL;
+ version_info_node = ggc_alloc_cleared_cgraph_function_version_info ();
+ version_info_node->this_node = node;
+
+ if (cgraph_fnver_htab == NULL)
+ cgraph_fnver_htab = htab_create_ggc (2, cgraph_fnver_htab_hash,
+ cgraph_fnver_htab_eq, NULL);
+
+ slot = htab_find_slot (cgraph_fnver_htab, version_info_node, INSERT);
+ gcc_assert (slot != NULL);
+ *slot = version_info_node;
+ return version_info_node;
+}
+
+/* Remove the cgraph_function_version_info and cgraph_node for DECL. This
+ DECL is a duplicate declaration. */
+void
+delete_function_version (tree decl)
+{
+ struct cgraph_node *decl_node = cgraph_get_node (decl);
+ struct cgraph_function_version_info *decl_v = NULL;
+
+ if (decl_node == NULL)
+ return;
+
+ decl_v = get_cgraph_node_version (decl_node);
+
+ if (decl_v == NULL)
+ return;
+
+ if (decl_v->prev != NULL)
+ decl_v->prev->next = decl_v->next;
+
+ if (decl_v->next != NULL)
+ decl_v->next->prev = decl_v->prev;
+
+ if (cgraph_fnver_htab != NULL)
+ htab_remove_elt (cgraph_fnver_htab, decl_v);
+
+ cgraph_remove_node (decl_node);
+}
+
+/* Record that DECL1 and DECL2 are semantically identical function
+ versions. */
+void
+record_function_versions (tree decl1, tree decl2)
+{
+ struct cgraph_node *decl1_node = cgraph_get_create_node (decl1);
+ struct cgraph_node *decl2_node = cgraph_get_create_node (decl2);
+ struct cgraph_function_version_info *decl1_v = NULL;
+ struct cgraph_function_version_info *decl2_v = NULL;
+ struct cgraph_function_version_info *before;
+ struct cgraph_function_version_info *after;
+
+ gcc_assert (decl1_node != NULL && decl2_node != NULL);
+ decl1_v = get_cgraph_node_version (decl1_node);
+ decl2_v = get_cgraph_node_version (decl2_node);
+
+ if (decl1_v != NULL && decl2_v != NULL)
+ return;
+
+ if (decl1_v == NULL)
+ decl1_v = insert_new_cgraph_node_version (decl1_node);
+
+ if (decl2_v == NULL)
+ decl2_v = insert_new_cgraph_node_version (decl2_node);
+
+ /* Chain decl2_v and decl1_v. All semantically identical versions
+ will be chained together. */
+
+ before = decl1_v;
+ after = decl2_v;
+
+ while (before->next != NULL)
+ before = before->next;
+
+ while (after->prev != NULL)
+ after= after->prev;
+
+ before->next = after;
+ after->prev = before;
+}
+
/* Macros to access the next item in the list of free cgraph nodes and
edges. */
#define NEXT_FREE_NODE(NODE) (NODE)->next
===================================================================
@@ -262,6 +262,8 @@ struct GTY((chain_next ("%h.next"), chain_prev ("%
/* ?? We should be able to remove this. We have enough bits in
cgraph to calculate it. */
unsigned tm_clone : 1;
+ /* True if this decl is a dispatcher for function versions. */
+ unsigned dispatcher_function : 1;
};
typedef struct cgraph_node *cgraph_node_ptr;
@@ -270,6 +272,47 @@ DEF_VEC_P(cgraph_node_ptr);
DEF_VEC_ALLOC_P(cgraph_node_ptr,heap);
DEF_VEC_ALLOC_P(cgraph_node_ptr,gc);
+/* Function Multiversioning info. */
+struct GTY(()) cgraph_function_version_info {
+ /* The cgraph_node for which the function version info is stored. */
+ struct cgraph_node *this_node;
+ /* Chains all the semantically identical function versions. The
+ first function in this chain is the version_info node of the
+ default function. */
+ struct cgraph_function_version_info *prev;
+ /* If this version node corresponds to a dispatcher for function
+ versions, this points to the version info node of the default
+ function, the first node in the chain. */
+ struct cgraph_function_version_info *next;
+ /* If this node corresponds to a function version, this points
+ to the dispatcher function decl, which is the function that must
+ be called to execute the right function version at run-time.
+
+ If this cgraph node is a dispatcher (if dispatcher_function is
+ true, in the cgraph_node struct) for function versions, this
+ points to resolver function, which holds the function body of the
+ dispatcher. The dispatcher decl is an alias to the resolver
+ function decl. */
+ tree dispatcher_resolver;
+};
+
+/* Get the cgraph_function_version_info node corresponding to node. */
+struct cgraph_function_version_info *
+ get_cgraph_node_version (struct cgraph_node *node);
+
+/* Insert a new cgraph_function_version_info node into cgraph_fnver_htab
+ corresponding to cgraph_node NODE. */
+struct cgraph_function_version_info *
+ insert_new_cgraph_node_version (struct cgraph_node *node);
+
+/* Record that DECL1 and DECL2 are semantically identical function
+ versions. */
+void record_function_versions (tree decl1, tree decl2);
+
+/* Remove the cgraph_function_version_info and cgraph_node for DECL. This
+ DECL is a duplicate declaration. */
+void delete_function_version (tree decl);
+
/* A cgraph node set is a collection of cgraph nodes. A cgraph node
can appear in multiple sets. */
struct cgraph_node_set_def
@@ -674,6 +717,11 @@ void cgraph_remove_node_duplication_hook (struct c
void cgraph_materialize_all_clones (void);
gimple cgraph_redirect_edge_call_stmt_to_callee (struct cgraph_edge *);
bool cgraph_propagate_frequency (struct cgraph_node *node);
+
+/* Initialize datastructures so DECL is a function in lowered gimple form.
+ IN_SSA is true if the gimple is in SSA. */
+basic_block init_lowered_empty_function (tree decl, bool in_ssa);
+
/* In cgraphbuild.c */
unsigned int rebuild_cgraph_edges (void);
void cgraph_rebuild_references (void);