From patchwork Tue Feb 26 21:52:25 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sriraman Tallam X-Patchwork-Id: 223412 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from sourceware.org (server1.sourceware.org [209.132.180.131]) by ozlabs.org (Postfix) with SMTP id DEEB22C007E for ; Wed, 27 Feb 2013 08:53:16 +1100 (EST) Comment: DKIM? See http://www.dkim.org DKIM-Signature: v=1; a=rsa-sha1; c=relaxed/relaxed; d=gcc.gnu.org; s=default; x=1362520397; h=Comment: DomainKey-Signature:Received:Received:Received:Received: MIME-Version:Received:Date:Message-ID:Subject:From:To: Content-Type:Mailing-List:Precedence:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:Sender:Delivered-To; bh=22FlBVE KARo49/U7ETU6CPraAig=; b=Zg9FcBQeaHloUMVDM/hkS1KXaf2pI4d3FJdyWTN f6QGdZ/LTRFQLLdiho9uqfhNpn59hOsCg2VNGyH2h1GNDZp+ffP0LdwPsj3N6z8e /MTA20NOT3SYkgRNKSMj9pcEsGDkUJo0zr89FfZng+rHdkcBvNBmKZujsqfJKIOY rgLU= Comment: DomainKeys? See http://antispam.yahoo.com/domainkeys DomainKey-Signature: a=rsa-sha1; q=dns; c=nofws; s=default; d=gcc.gnu.org; h=Received:Received:X-SWARE-Spam-Status:X-Spam-Check-By:Received:Received:X-Google-DKIM-Signature:MIME-Version:X-Received:Received:Date:Message-ID:Subject:From:To:Content-Type:X-Gm-Message-State:X-IsSubscribed:Mailing-List:Precedence:List-Id:List-Unsubscribe:List-Archive:List-Post:List-Help:Sender:Delivered-To; b=ucKnicexbv5LmKn8KW5go1UuR3bQymOuvzVrOU8R2jmGCAid0G+S7R4+lhbVGr KD0iPfAZJyg3iAA9oyza5WfZHQ9Jxb98isNx5y7aSKDAbPYymekbwu+KCZ7DeYo/ dYv9lnc0GNtILevTHlifdCrj1vxP5qs5L8xHo+BQt1IGY=; Received: (qmail 19378 invoked by alias); 26 Feb 2013 21:53:07 -0000 Received: (qmail 19369 invoked by uid 22791); 26 Feb 2013 21:53:06 -0000 X-SWARE-Spam-Status: No, hits=-5.0 required=5.0 tests=AWL, BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, KHOP_RCVD_TRUST, RCVD_IN_DNSWL_LOW, RCVD_IN_HOSTKARMA_YE, RP_MATCHES_RCVD, TW_CP, TW_FN, TW_TM, TW_VF X-Spam-Check-By: sourceware.org Received: from mail-oa0-f52.google.com (HELO mail-oa0-f52.google.com) (209.85.219.52) by sourceware.org (qpsmtpd/0.43rc1) with ESMTP; Tue, 26 Feb 2013 21:52:26 +0000 Received: by mail-oa0-f52.google.com with SMTP id k14so6249254oag.39 for ; Tue, 26 Feb 2013 13:52:25 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=mime-version:x-received:date:message-id:subject:from:to :content-type:x-gm-message-state; bh=XDDC6Tiyfh0y+smvVM1sr79ln8k7nV0v9dYQQb365sA=; b=lha1XH36HHSuXVwDR+nclBB3gNmWYB1TmzH2Hfmb+UPFujrBSa+zBgarvFknVst29T mT+vtxbuIvBn/gLWxsKR1qDASg5SLIPaAtw1abg3KPs3p55A8pITWNr1ydsfcq/ilakT b9cQ+NpJRdmSnw8G5wt7AVu7ncUs53KSKZzcpy6B362G/96zm7kLmp2xOmOyHS9oj+dN aQ/oH6nuS12EFD7f8gAws7CZVO590L5fv11uhNmD9e+UdF8Pl7elGfMbwJESoW2yC5IY ptAgv57+bElllt2N8p8eLibdV6TMe8Pl57hfMsUY8zyvLWOxORYTLRGWR1qRGNP7b20K v5lg== MIME-Version: 1.0 X-Received: by 10.182.156.20 with SMTP id wa20mr13133424obb.59.1361915545575; Tue, 26 Feb 2013 13:52:25 -0800 (PST) Received: by 10.182.108.9 with HTTP; Tue, 26 Feb 2013 13:52:25 -0800 (PST) Date: Tue, 26 Feb 2013 13:52:25 -0800 Message-ID: Subject: [google][4.7] Backport Function Multiversioning feature from trunk. From: Sriraman Tallam To: GCC Patches , David Li X-Gm-Message-State: ALoCoQmj1qTSWCxkjaTym5ADhvV89IUPk3KmGiaYDBd0+jO+/xF+lw+XMpWIUGLTGs9oZ73/bxLRusYqWBmfPGotqR6wPwoCuYzK854iPSLpVxi069LNxmVeJ7kD1KOweZynXHRrGLn1pwqut8y9P0qdyX+/rVNbBCmZCrauaaBh8nhGMm6/IVtkd1g2WDvdJVyBB4O8t/Vm X-IsSubscribed: yes Mailing-List: contact gcc-patches-help@gcc.gnu.org; run by ezmlm Precedence: bulk List-Id: List-Unsubscribe: List-Archive: List-Post: List-Help: Sender: gcc-patches-owner@gcc.gnu.org Delivered-To: mailing list gcc-patches@gcc.gnu.org Hi, This patch backports the Function Multiversioning feature checked into trunk. This trunk revisions merged are : r193204,193486,193555,194730,194818,194828,195584,195967,196033 The links to changes in each revision: r193204: http://gcc.gnu.org/ml/gcc-patches/2012-11/msg00470.html r193486: http://gcc.gnu.org/ml/gcc-patches/2012-11/msg00956.html r193555: http://gcc.gnu.org/ml/gcc-patches/2012-11/msg01369.html r194730: http://gcc.gnu.org/ml/gcc-patches/2012-12/msg01417.html r194818: http://gcc.gnu.org/ml/gcc-patches/2013-01/msg00060.html r194828: http://gcc.gnu.org/ml/gcc-patches/2013-01/msg00070.html r195584: http://gcc.gnu.org/ml/gcc-patches/2013-01/msg01428.html r195967: http://gcc.gnu.org/ml/gcc-patches/2013-02/msg00504.html r196033: http://gcc.gnu.org/ml/gcc-patches/2013-02/msg00659.html Ok to commit? Thanks Sri This patch backports the Function Multiversioning feature checked into trunk. This trunk revisions merged are : r193204,193486,193555,194730,194818,194828,195584,195967,196033 The links to changes in each revision: r193204: http://gcc.gnu.org/ml/gcc-patches/2012-11/msg00470.html r193486: http://gcc.gnu.org/ml/gcc-patches/2012-11/msg00956.html r193555: http://gcc.gnu.org/ml/gcc-patches/2012-11/msg01369.html r194730: http://gcc.gnu.org/ml/gcc-patches/2012-12/msg01417.html r194818: http://gcc.gnu.org/ml/gcc-patches/2013-01/msg00060.html r194828: http://gcc.gnu.org/ml/gcc-patches/2013-01/msg00070.html r195584: http://gcc.gnu.org/ml/gcc-patches/2013-01/msg01428.html r195967: http://gcc.gnu.org/ml/gcc-patches/2013-02/msg00504.html r196033: http://gcc.gnu.org/ml/gcc-patches/2013-02/msg00659.html Property changes on: . Index: gcc/target.def =================================================================== --- gcc/target.def (revision 196293) +++ gcc/target.def (working copy) @@ -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_" Index: gcc/cp/decl2.c =================================================================== --- gcc/cp/decl2.c (revision 196293) +++ gcc/cp/decl2.c (working copy) @@ -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))) Index: gcc/cp/call.c =================================================================== --- gcc/cp/call.c (revision 196293) +++ gcc/cp/call.c (working copy) @@ -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 Index: gcc/cp/cp-tree.h =================================================================== --- gcc/cp/cp-tree.h (revision 196293) +++ gcc/cp/cp-tree.h (working copy) @@ -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); Index: gcc/cp/class.c =================================================================== --- gcc/cp/class.c (revision 196293) +++ gcc/cp/class.c (working copy) @@ -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 Index: gcc/cp/decl.c =================================================================== --- gcc/cp/decl.c (revision 196293) +++ gcc/cp/decl.c (working copy) @@ -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; Index: gcc/config/i386/i386.c =================================================================== --- gcc/config/i386/i386.c (revision 196293) +++ gcc/config/i386/i386.c (working copy) @@ -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 % 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 */ + 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 % 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 Index: gcc/cgraphunit.c =================================================================== --- gcc/cgraphunit.c (revision 196293) +++ gcc/cgraphunit.c (working copy) @@ -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); Index: gcc/doc/extend.texi =================================================================== --- gcc/doc/extend.texi (revision 196293) +++ gcc/doc/extend.texi (working copy) @@ -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 Index: gcc/doc/tm.texi =================================================================== --- gcc/doc/tm.texi (revision 196293) +++ gcc/doc/tm.texi (working copy) @@ -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 Index: gcc/doc/tm.texi.in =================================================================== --- gcc/doc/tm.texi.in (revision 196293) +++ gcc/doc/tm.texi.in (working copy) @@ -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 Index: gcc/tree.h =================================================================== --- gcc/tree.h (revision 196293) +++ gcc/tree.h (working copy) @@ -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 Index: gcc/testsuite/g++.dg/ext/mv12.C =================================================================== --- gcc/testsuite/g++.dg/ext/mv12.C (revision 196273) +++ gcc/testsuite/g++.dg/ext/mv12.C (working copy) @@ -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" Index: gcc/testsuite/g++.dg/ext/mv12.h =================================================================== --- gcc/testsuite/g++.dg/ext/mv12.h (revision 196273) +++ gcc/testsuite/g++.dg/ext/mv12.h (working copy) @@ -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"))); Index: gcc/cgraph.c =================================================================== --- gcc/cgraph.c (revision 196293) +++ gcc/cgraph.c (working copy) @@ -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 Index: gcc/cgraph.h =================================================================== --- gcc/cgraph.h (revision 196293) +++ gcc/cgraph.h (working copy) @@ -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);